Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 282d894

Browse files
committedJul 27, 2018
applet.smia: new (partially functional) applet.
1 parent 2e1c627 commit 282d894

File tree

5 files changed

+1046
-0
lines changed

5 files changed

+1046
-0
lines changed
 

‎software/glasgow/applet/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ def run(self, device, args):
4242
from .i2c.eeprom import I2CEEPROMApplet
4343
from .program_ice40 import ProgramICE40Applet
4444
from .selftest import SelfTestApplet
45+
from .smia import SMIAApplet
4546
from .uart import UARTApplet

‎software/glasgow/applet/smia.py

Whitespace-only changes.
+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import logging
2+
import time
3+
from migen import *
4+
5+
from .. import *
6+
from ..i2c_master import I2CMasterApplet
7+
from .smia_cci import SMIACCIInterface, SMIACamera
8+
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
class SMIASubtarget(Module):
14+
def __init__(self, pads, reset):
15+
self.comb += [
16+
pads.xshutdown_t.oe.eq(1),
17+
pads.extclk_t.oe.eq(1),
18+
]
19+
20+
reset_cyc = 1 << 16
21+
extclk_cyc = 2
22+
23+
reset_timer = Signal(max=reset_cyc, reset=reset_cyc - 1)
24+
self.sync += [
25+
If(reset,
26+
reset_timer.eq(reset_timer.reset)
27+
).Elif(reset_timer != 0,
28+
reset_timer.eq(reset_timer - 1)
29+
)
30+
]
31+
self.comb += pads.xshutdown_t.o.eq(reset_timer == 0)
32+
33+
extclk_timer = Signal(max=extclk_cyc, reset=extclk_cyc - 1)
34+
self.sync += [
35+
If(extclk_timer == 0,
36+
pads.extclk_t.o.eq(~pads.extclk_t.o),
37+
extclk_timer.eq(extclk_timer.reset)
38+
).Else(
39+
extclk_timer.eq(extclk_timer - 1)
40+
)
41+
]
42+
43+
44+
class CCP2Subtarget(Module):
45+
def __init__(self, pin_clk, pin_data, arbiter, errors):
46+
clk = Signal()
47+
data0 = Signal()
48+
data1 = Signal()
49+
self.specials += [
50+
Instance("SB_IO",
51+
p_PIN_TYPE=C(0b000001, 6), # PIN_INPUT
52+
io_PACKAGE_PIN=pin_clk,
53+
o_D_IN_0=clk,
54+
),
55+
Instance("SB_IO",
56+
p_PIN_TYPE=C(0b000000, 6), # PIN_INPUT_DDR
57+
io_PACKAGE_PIN=pin_data,
58+
i_INPUT_CLK=clk,
59+
o_D_IN_0=data0,
60+
o_D_IN_1=data1,
61+
),
62+
]
63+
64+
self.clock_domains.cd_pix = ClockDomain()
65+
self.comb += self.cd_pix.clk.eq(clk)
66+
67+
shreg = Signal(33)
68+
bitno = Signal(3)
69+
self.sync.pix += [
70+
shreg.eq(Cat(shreg[2:], data0, data1)),
71+
If(shreg[2:26] == C(0x0000ff, 24),
72+
bitno.eq(0)
73+
).Elif(shreg[3:27] == C(0x0000ff, 24),
74+
bitno.eq(1)
75+
).Else(
76+
bitno.eq(bitno + 2)
77+
)
78+
]
79+
80+
in_fifo = arbiter.get_in_fifo(1, clock_domain=self.cd_pix)
81+
self.sync.pix += [
82+
If(bitno[0],
83+
in_fifo.din.eq(shreg[1:9])
84+
).Else(
85+
in_fifo.din.eq(shreg[0:8])
86+
),
87+
in_fifo.we.eq((bitno & ~1) == 0),
88+
]
89+
90+
91+
class SMIAApplet(I2CMasterApplet, name="smia"):
92+
logger = logger
93+
help = "capture image data from SMIA cameras"
94+
description = """
95+
Configure SMIA cameras via the CCI interface and capture image data via the CCP2 interface.
96+
"""
97+
98+
@classmethod
99+
def add_build_arguments(cls, parser, access):
100+
super().add_build_arguments(parser, access)
101+
102+
access.add_pin_argument(parser, "xshutdown", required=True)
103+
access.add_pin_argument(parser, "extclk", required=True)
104+
105+
access.add_pin_argument(parser, "clk", required=True)
106+
access.add_pin_argument(parser, "data", required=True)
107+
108+
def build(self, target, args):
109+
super().build(target, args)
110+
111+
iface = self.mux_interface
112+
reset, self.__addr_reset = target.registers.add_rw(1, reset=1)
113+
target.submodules += SMIASubtarget(
114+
pads=iface.get_pads(args, pins=("xshutdown", "extclk")),
115+
reset=reset,
116+
)
117+
118+
errors, self.__addr_errors = target.registers.add_ro()
119+
target.submodules += CCP2Subtarget(
120+
pin_clk=iface._pins[args.pin_clk],
121+
pin_data=iface._pins[args.pin_data],
122+
arbiter=iface._fx2_arbiter,
123+
errors=errors,
124+
)
125+
126+
@classmethod
127+
def add_run_arguments(cls, parser, access):
128+
super().add_run_arguments(parser, access)
129+
130+
parser.add_argument("-i", "--show-info", action="store_true", default=False,
131+
help="print camera information (model, manufacturer, etc)")
132+
parser.add_argument("-l", "--show-limits", action="store_true", default=False,
133+
help="print camera limits and capabilities (frequencies, gains, sizes, etc)")
134+
parser.add_argument("-c", "--show-clocking", action="store_true", default=False,
135+
help="print camera clocking configuration")
136+
137+
def run(self, device, args):
138+
i2c_iface = super().run(device, args, interactive=False)
139+
cci_iface = SMIACCIInterface(i2c_iface, self.logger)
140+
camera = SMIACamera(cci_iface, self.logger, 15e6)
141+
142+
device.set_voltage("A", 1.8)
143+
device.set_voltage("B", 2.7)
144+
145+
device.write_register(self.__addr_reset, 1)
146+
i2c_iface.reset()
147+
time.sleep(0.050)
148+
device.write_register(self.__addr_reset, 0)
149+
time.sleep(0.050)
150+
151+
if args.show_info:
152+
camera.show_info()
153+
154+
if camera.get_smia_version() not in ((0, 9), (1, 0)):
155+
self.logger.error("unsupported SMIA version %d.%d", *camera.get_smia_version())
156+
if camera.get_smia_version() == (0, 0):
157+
self.logger.error("camera responded with SMIA version 0.0; is the power on?")
158+
self.logger.error("some cameras pull SDA/SCL lines to ground until both power "
159+
"supplies are on, in violation of the SMIA specification")
160+
return
161+
162+
if args.show_limits:
163+
camera.show_limits()
164+
165+
camera.set_ccp_data_format("RAW8")
166+
167+
self.logger.info("autoconfiguring clocking")
168+
def clocking_constraints(config):
169+
return (config["op_sys_clk_freq_mhz"] <= 50.0 and
170+
config["vt_sys_clk_freq_mhz"] == config["op_sys_clk_freq_mhz"])
171+
if not camera.autoconfigure_clocking(clocking_constraints):
172+
self.logger.error("cannot automatically configure clocking")
173+
174+
if args.show_clocking:
175+
camera.show_clocking_configuration()
176+
177+
camera.set("test_pattern_mode", "ColourBars")
178+
camera.set("test_data_red", 0b10101010)
179+
camera.set("test_data_greenR", 0x00)
180+
camera.set("test_data_blue", 0x00)
181+
camera.set("test_data_greenB", 0x00)
182+
camera.start_streaming()
183+
184+
print(device.bulk_read(8, 16384).hex())

‎software/glasgow/applet/smia/smia_cci.py

+783
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import os
2+
import tempfile
3+
from migen import *
4+
from migen.build.generic_platform import *
5+
from migen.build.platforms.ice40_hx8k_b_evn import Platform as HX8KEvaluationPlatform
6+
7+
8+
_ext = [
9+
("clk_in", 0, Pins("C2")), # DP02B
10+
("clk_out", 0, Pins("D1")),
11+
("data_in", 0, Pins("J1")), # DP14B
12+
("data_out", 0, Pins("K3")),
13+
]
14+
15+
16+
class SubLVDSBuffer(Module):
17+
def __init__(self):
18+
self.platform = HX8KEvaluationPlatform()
19+
self.platform.add_extension(_ext)
20+
21+
self.clock_domains.cd_sys = ClockDomain()
22+
23+
counter = Signal(22)
24+
self.sync += counter.eq(counter - 1)
25+
26+
led = self.platform.request("user_led")
27+
self.comb += led.eq(counter[counter.nbits - 1])
28+
29+
clk = Signal()
30+
data = Signal()
31+
clk_in = self.platform.request("clk_in")
32+
data_in = self.platform.request("data_in")
33+
clk_out = self.platform.request("clk_out")
34+
data_out = self.platform.request("data_out")
35+
36+
self.comb += self.cd_sys.clk.eq(~clk)
37+
38+
clk_2 = Signal()
39+
self.sync += [
40+
clk_2.eq(~clk_2)
41+
]
42+
43+
self.specials += [
44+
Instance("SB_IO",
45+
p_PIN_TYPE=C(0b000001, 6), # PIN_INPUT
46+
p_IO_STANDARD="SB_LVDS_INPUT",
47+
io_PACKAGE_PIN=clk_in,
48+
o_D_IN_0=clk,
49+
),
50+
Instance("SB_IO",
51+
p_PIN_TYPE=C(0b011000, 6), # PIN_OUTPUT
52+
io_PACKAGE_PIN=clk_out,
53+
i_D_OUT_0=clk_2,
54+
),
55+
Instance("SB_IO",
56+
p_PIN_TYPE=C(0b000001, 6), # PIN_INPUT
57+
p_IO_STANDARD="SB_LVDS_INPUT",
58+
io_PACKAGE_PIN=data_in,
59+
o_D_IN_0=data,
60+
),
61+
Instance("SB_IO",
62+
p_PIN_TYPE=C(0b011000, 6), # PIN_OUTPUT
63+
io_PACKAGE_PIN=data_out,
64+
i_D_OUT_0=data,
65+
),
66+
]
67+
68+
def build_and_load(self, **kwargs):
69+
build_dir = tempfile.mkdtemp(prefix="sublvds_")
70+
self.platform.build(self, build_dir=build_dir, **kwargs)
71+
72+
programmer = self.platform.create_programmer()
73+
programmer.load_bitstream(os.path.join(build_dir, "top.bin"))
74+
75+
76+
if __name__ == "__main__":
77+
sub_lvds_buffer = SubLVDSBuffer()
78+
sub_lvds_buffer.build_and_load()

0 commit comments

Comments
 (0)
Please sign in to comment.