lint
diff --git a/configure.py b/configure.py index cc0ce65..28479fb 100755 --- a/configure.py +++ b/configure.py
@@ -6,7 +6,6 @@ from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL) -filler_project_url = 'https://github.com/mattvenn/wokwi_filler' tmp_dir = '/tmp/tt' DEFAULT_NUM_PROJECTS = 498 @@ -15,8 +14,9 @@ projects_db = "projects.pkl" - def __init__(self, update_cache=False, update_single=None): + def __init__(self, update_cache=False, update_single=None, test=False): self.default_project = 0 + self.test = test if update_cache: self.update_cache(update_single) else: @@ -92,12 +92,22 @@ def get_wokwi_ids(self): return self.wokwi_ids + def get_giturl(self, id): + try: + return self.get_project_urls()[id] + except IndexError: + return self.get_project_urls()[self.default_project] + @classmethod def build_wokwi_url(Project, wokwi_id): return "https://wokwi.com/projects/{}".format(wokwi_id) def get_project_urls(self): - from project_urls import project_urls + if self.test: + from project_urls_test import project_urls, filler_project_url + else: + from project_urls import project_urls, filler_project_url + return [filler_project_url] + project_urls # the latest artifact isn't necessarily the one related to the latest commit, as github @@ -149,7 +159,7 @@ exit(1) else: # only get artifacts called GDS - artifacts = [ a for a in data['artifacts'] if a['name'] == 'GDS'] + artifacts = [a for a in data['artifacts'] if a['name'] == 'GDS'] logging.debug("found {} artifacts".format(len(artifacts))) download_url = Projects.get_most_recent_action_url(commits, artifacts) @@ -294,13 +304,14 @@ .slow_clk (io_out[10]), .set_clk_div (io_in[11]), - .scan_clk (clk[0]), + .scan_clk_out (clk[0]), + .scan_clk_in (clk[NUM_MACROS]), .scan_data_out (data[0]), .scan_data_in (data[NUM_MACROS]), .scan_select (scan[0]), .scan_latch_en (latch[0]), - .la_scan_clk (la_data_in[0]), + .la_scan_clk_in (la_data_in[0]), .la_scan_data_in (la_data_in[1]), .la_scan_data_out (la_data_out[0]), .la_scan_select (la_data_in[2]), @@ -313,6 +324,7 @@ """ lesson_template = """ + // {giturl} {name} #(.NUM_IOS(8)) {instance} ( .clk_in (clk [{id}]), .data_in (data [{id}]), @@ -337,7 +349,7 @@ for i in range(self.num_projects): logging.debug("instance {} {}".format(i, self.projects.get_macro_name(i))) # instantiate template - instance = lesson_template.format(instance=self.projects.get_macro_instance(i), name=self.projects.get_macro_name(i), id=i, next_id=i + 1) + instance = lesson_template.format(giturl=self.projects.get_giturl(i), instance=self.projects.get_macro_instance(i), name=self.projects.get_macro_name(i), id=i, next_id=i + 1) fh.write(instance) fh.write(post) @@ -382,6 +394,7 @@ parser.add_argument('--update-single', help='only fetch a single repo for debug') parser.add_argument('--update-caravel', help='configure caravel for build', action='store_const', const=True) parser.add_argument('--limit-num-projects', help='only configure for the first n projects', type=int, default=DEFAULT_NUM_PROJECTS) + parser.add_argument('--test', help='use test projects', action='store_const', const=True) parser.add_argument('--debug', help="debug logging", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO) args = parser.parse_args() @@ -402,7 +415,7 @@ ch.setFormatter(log_format) log.addHandler(ch) - projects = Projects(update_cache=args.update_projects, update_single=args.update_single) + projects = Projects(update_cache=args.update_projects, test=args.test, update_single=args.update_single) caravel = CaravelConfig(projects, num_projects=args.limit_num_projects) if args.update_caravel:
diff --git a/openlane/scan_controller/base.sdc b/openlane/scan_controller/base.sdc new file mode 100644 index 0000000..7324dd0 --- /dev/null +++ b/openlane/scan_controller/base.sdc
@@ -0,0 +1,70 @@ +# SDC file, in TCL +# declare clocks and relationships between them +# Create main clock of the scan controller, locally named the same clock +create_clock [get_ports $::env(CLOCK_PORT)] -name $::env(CLOCK_PORT) -period $::env(CLOCK_PERIOD) + +# Create our generated clock that drives the scan chain +# derived from clk, present on edge 3 5 7 +# multicycle path, only data transition on falling edge of scan clock +create_generated_clock -edges {3 5 7} -source $::env(CLOCK_PORT) -name clk_scan_out [get_ports scan_clk_out] +set_multicycle_path -start -hold -from [get_clocks $::env(CLOCK_PORT)] -to [get_clocks clk_scan_out] 1 + +# Create feedback clock - scan_clk_in. we don't know what it will arrive, but we know the period +create_clock [get_ports scan_clk_in] -name clk_scan_in -period [expr {$::env(CLOCK_PERIOD) * 2}] + +# CDC +# receiving data into shift register and clocked by clk_scan_in +# constrain to a reasonable range +set_min_delay 0.25 -from [get_clocks clk_scan_in] -to [get_clocks $::env(CLOCK_PORT)] +set_max_delay [expr {$::env(CLOCK_PERIOD) / 2}] -from [get_clocks clk_scan_in] -to [get_clocks $::env(CLOCK_PORT)] + +# IO delays +# Scan chain input 0.5 ns setup time, 0.5 ns hold time +set_input_delay -min 0.5 -clock [get_clocks clk_scan_in] [get_ports {scan_data_in}] +set_input_delay -max 0.5 -clock [get_clocks clk_scan_in] [get_ports {scan_data_in}] + +# Scan chain output 1.5 ns setup time, 1.5 ns hold time +# scan_data_out cannot change in this window around clk_scan_out rising edge +set_output_delay -min -1.5 -clock [get_clocks clk_scan_out] [get_ports {scan_data_out}] +set_output_delay -max 1.5 -clock [get_clocks clk_scan_out] [get_ports {scan_data_out}] + +# Limit the max output delay on other outputs +# Probably not important, but useful because then these paths will be reported +set_output_delay -max 1.5 -clock [get_clocks $::env(CLOCK_PORT)] [get_ports {scan_select scan_latch_en}] + +# All the other IOs we consider asynchronous so ... no point in constraining +# https://open-source-silicon.slack.com/archives/C02K4RD241Y/p1661983399157329?thread_ts=1661978191.926169&cid=C02K4RD241Y +set_false_path -from [get_ports {active_select inputs set_clk_div la_* driver_sel}] + + +# Misc +# most copied from default OpenLane SDC +set_max_fanout $::env(SYNTH_MAX_FANOUT) [current_design] + +if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL)] } { + set ::env(SYNTH_CLK_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL) +} + +if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL_PIN)] } { + set ::env(SYNTH_CLK_DRIVING_CELL_PIN) $::env(SYNTH_DRIVING_CELL_PIN) +} + +set_driving_cell -lib_cell $::env(SYNTH_DRIVING_CELL) -pin $::env(SYNTH_DRIVING_CELL_PIN) [get_ports {scan_data_in}] +set_driving_cell -lib_cell $::env(SYNTH_CLK_DRIVING_CELL) -pin $::env(SYNTH_CLK_DRIVING_CELL_PIN) [get_ports "$::env(CLOCK_PORT) scan_clk_in"] + +set cap_load [expr $::env(SYNTH_CAP_LOAD) / 1000.0] +puts "\[INFO\]: Setting load to: $cap_load" +set_load $cap_load [all_outputs] + +# clock jitter +puts "\[INFO\]: Setting clock uncertainity to: $::env(SYNTH_CLOCK_UNCERTAINITY)" +set_clock_uncertainty $::env(SYNTH_CLOCK_UNCERTAINITY) [get_clocks "$::env(CLOCK_PORT) clk_scan_in clk_scan_out"] + +# clock slew +puts "\[INFO\]: Setting clock transition to: $::env(SYNTH_CLOCK_TRANSITION)" +set_clock_transition $::env(SYNTH_CLOCK_TRANSITION) [get_clocks "$::env(CLOCK_PORT) clk_scan_in clk_scan_out"] + +# make everything worse by SYNTH_TIMING_DERATE +puts "\[INFO\]: Setting timing derate to: [expr {$::env(SYNTH_TIMING_DERATE) * 100}] %" +set_timing_derate -early [expr {1-$::env(SYNTH_TIMING_DERATE)}] +set_timing_derate -late [expr {1+$::env(SYNTH_TIMING_DERATE)}]
diff --git a/openlane/scan_controller/config.tcl b/openlane/scan_controller/config.tcl index b51552f..c52bd12 100644 --- a/openlane/scan_controller/config.tcl +++ b/openlane/scan_controller/config.tcl
@@ -31,6 +31,11 @@ set ::env(CLOCK_PERIOD) "10" set ::env(CLOCK_PORT) "clk" +set ::env(BASE_SDC_FILE) $::env(DESIGN_DIR)/base.sdc + +set ::env(SYNTH_CLOCK_UNCERTAINITY) 0.20 +set ::env(SYNTH_CLOCK_TRANSITION) 0.15 + # macro needs to work inside Caravel, so can't be core and can't use metal 5 set ::env(DESIGN_IS_CORE) 0 set ::env(RT_MAX_LAYER) {met4}
diff --git a/project_urls.py b/project_urls.py index 5853b70..b1bc202 100644 --- a/project_urls.py +++ b/project_urls.py
@@ -1,4 +1,4 @@ -# name must be valid verilog module name set in scan_wrapper.v +filler_project_url = 'https://github.com/mattvenn/wokwi_filler' project_urls = [ 'https://github.com/mattvenn/wokwi-verilog-gds-test', 'https://github.com/mattvenn/animation_tinytapeout_demo',
diff --git a/project_urls_test.py b/project_urls_test.py new file mode 100644 index 0000000..a10723a --- /dev/null +++ b/project_urls_test.py
@@ -0,0 +1,5 @@ +filler_project_url = 'https://github.com/mattvenn/tinytapeout-test-straight' +project_urls = [ + 'https://github.com/mattvenn/tinytapeout-7seg-seconds-counter', + 'https://github.com/mattvenn/tinytapeout-test-invert', + ]
diff --git a/verilog/dv/scan_controller/test_scan_controller.gtkw b/verilog/dv/scan_controller/test_scan_controller.gtkw index f4509d8..c90cbc9 100644 --- a/verilog/dv/scan_controller/test_scan_controller.gtkw +++ b/verilog/dv/scan_controller/test_scan_controller.gtkw
@@ -1,19 +1,18 @@ [*] [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI -[*] Sat Aug 27 16:09:50 2022 +[*] Wed Aug 31 19:31:00 2022 [*] [dumpfile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller/test_scan_controller.vcd" -[dumpfile_mtime] "Sat Aug 27 16:09:19 2022" -[dumpfile_size] 86352838 +[dumpfile_mtime] "Wed Aug 31 19:30:43 2022" +[dumpfile_size] 14402588 [savefile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller/test_scan_controller.gtkw" [timestart] 0 [size] 1848 1016 [pos] -1 -1 -*-27.000000 91200000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +*-30.000000 3209000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] test_scan_controller_tb. [treeopen] test_scan_controller_tb.user_project_wrapper. [treeopen] test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0. -[treeopen] test_scan_controller_tb.user_project_wrapper.scan_wrapper_340805072482992722_12. [sst_width] 507 [signals_width] 265 [sst_expanded] 1 @@ -21,76 +20,19 @@ @28 test_scan_controller_tb.clk test_scan_controller_tb.reset -@24 -test_scan_controller_tb.user_project_wrapper.active_select[8:0] -@c00022 -test_scan_controller_tb.user_project_wrapper.inputs[7:0] -@28 -(0)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(1)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(2)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(3)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(4)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(5)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(6)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -(7)test_scan_controller_tb.user_project_wrapper.inputs[7:0] -@1401200 --group_end -@c00022 -test_scan_controller_tb.user_project_wrapper.outputs[7:0] -@28 -(0)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(1)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(2)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(3)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(4)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(5)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(6)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -(7)test_scan_controller_tb.user_project_wrapper.outputs[7:0] -@1401200 --group_end -@28 -test_scan_controller_tb.user_project_wrapper.ready -@800200 --scan controller -@28 -test_scan_controller_tb.user_project_wrapper.scan_controller.scan_clk -test_scan_controller_tb.user_project_wrapper.scan_controller.state[2:0] -@22 -test_scan_controller_tb.user_project_wrapper.scan_controller.current_design[8:0] -@1000200 --scan controller -@c00200 --module 0 -@28 -test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.data_in -test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.data_out -@800022 -test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -@28 -(0)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(1)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(2)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(3)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(4)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(5)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(6)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -(7)test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_data_in[7:0] -test_scan_controller_tb.user_project_wrapper.scan_wrapper_339501025136214612_0.scan_select_in -@1001200 --group_end -@1401200 --module 0 -@800200 --scan chain -@28 -test_scan_controller_tb.user_project_wrapper.scan_controller.clk -test_scan_controller_tb.user_project_wrapper.scan_controller.scan_clk +test_scan_controller_tb.user_project_wrapper.scan_controller.scan_clk_in +test_scan_controller_tb.user_project_wrapper.scan_controller.scan_clk_out test_scan_controller_tb.user_project_wrapper.scan_controller.scan_data_in test_scan_controller_tb.user_project_wrapper.scan_controller.scan_data_out -test_scan_controller_tb.user_project_wrapper.scan_controller.scan_latch_enable +test_scan_controller_tb.user_project_wrapper.scan_controller.scan_latch_en test_scan_controller_tb.user_project_wrapper.scan_controller.scan_select -@1000200 --scan chain +@22 +test_scan_controller_tb.user_project_wrapper.scan_controller.inputs[7:0] +test_scan_controller_tb.user_project_wrapper.scan_controller.outputs[7:0] +test_scan_controller_tb.user_project_wrapper.scan_controller.active_select[8:0] +@28 +test_scan_controller_tb.user_project_wrapper.scan_controller.ready +@29 +test_scan_controller_tb.user_project_wrapper.scan_controller.proj_done [pattern_trace] 1 [pattern_trace] 0
diff --git a/verilog/dv/scan_controller/test_scan_controller.py b/verilog/dv/scan_controller/test_scan_controller.py index ba1da90..3cfe4da 100644 --- a/verilog/dv/scan_controller/test_scan_controller.py +++ b/verilog/dv/scan_controller/test_scan_controller.py
@@ -39,14 +39,13 @@ dut.reset.value = 1 dut.set_clk_div.value = 0 - dut.driver_sel.value = 0b01 # internal controller - dut.active_select.value = 12 # 7 seg seconds + dut.driver_sel.value = 0b10 # internal controller + dut.active_select.value = 1 # 7 seg seconds await ClockCycles(dut.clk, 10) dut.reset.value = 0 dut.inputs.value = 0 dut.set_clk_div.value = 1 # lock in the new clock divider value await ClockCycles(dut.clk, 1) -# dut.set_clk_div.value = 0 dut.inputs.value = 0 # reset: set bit 1 high, wait for one cycle of slow_clk, then set bit 1 low @@ -60,7 +59,40 @@ await FallingEdge(dut.slow_clk) for i in range(2): print("clock {:2} 7seg {}".format(i, decode_seg(dut.seven_seg.value))) - #assert decode_seg(dut.seven_seg.value) == i - #await single_cycle(dut) + assert decode_seg(dut.seven_seg.value) == i await FallingEdge(dut.slow_clk) + print("straight test") + dut.set_clk_div.value = 0 # no clock div + dut.active_select.value = 0 # straight + await FallingEdge(dut.ready) + for i in range(11): + dut.inputs.value = i + await FallingEdge(dut.ready) + print(i, int(dut.outputs)) + if i > 0: + assert i == int(dut.outputs) + 1 + + print("invert test") + dut.active_select.value = 2 # invert + dut.inputs.value = 0 + await FallingEdge(dut.ready) + await FallingEdge(dut.ready) + for i in range(11): + dut.inputs.value = i + await FallingEdge(dut.ready) + print(i, int(dut.outputs)) + if i > 0: + assert 256 - i == int(dut.outputs) + + for design in range(10): # next 10 designs are all straight + print("straight test at pos {}".format(design)) + dut.active_select.value = 3 + design + await FallingEdge(dut.ready) + for i in range(11): + dut.inputs.value = i + await FallingEdge(dut.ready) + print(i, int(dut.outputs)) + if i > 0: + assert i == int(dut.outputs) + 1 +
diff --git a/verilog/dv/scan_controller_int/scan_controller_int.gtkw b/verilog/dv/scan_controller_int/scan_controller_int.gtkw index 30bde20..2007495 100644 --- a/verilog/dv/scan_controller_int/scan_controller_int.gtkw +++ b/verilog/dv/scan_controller_int/scan_controller_int.gtkw
@@ -1,23 +1,23 @@ [*] [*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI -[*] Sun Aug 28 04:38:22 2022 +[*] Wed Aug 31 20:59:40 2022 [*] [dumpfile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller_int/scan_controller_tb.vcd" -[dumpfile_mtime] "Sun Aug 28 04:37:55 2022" -[dumpfile_size] 1813865 +[dumpfile_mtime] "Wed Aug 31 20:58:10 2022" +[dumpfile_size] 830785 [savefile] "/home/matt/work/asic-workshop/shuttle7/tinytapeout-mpw7/verilog/dv/scan_controller_int/scan_controller_int.gtkw" [timestart] 0 [size] 1848 1016 [pos] -1 -1 -*-29.000000 696514800 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +*-28.000000 744000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [treeopen] scan_controller_tb. [treeopen] scan_controller_tb.uut. [treeopen] scan_controller_tb.uut.mprj. [treeopen] scan_controller_tb.uut.mprj.scan_controller. -[sst_width] 423 -[signals_width] 261 +[sst_width] 708 +[signals_width] 329 [sst_expanded] 1 -[sst_vpaned_height] 254 +[sst_vpaned_height] 646 @28 scan_controller_tb.clk @22 @@ -32,10 +32,14 @@ @28 scan_controller_tb.set_clk_div scan_controller_tb.slow_clk +scan_controller_tb.uut.mprj.scan_controller.slow_clk +scan_controller_tb.uut.mprj.scan_controller.slow_clk_ena +scan_controller_tb.uut.mprj.scan_controller.reset +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.reset +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.clk @800200 -scan chain @28 -scan_controller_tb.uut.mprj.scan_controller.scan_clk scan_controller_tb.uut.mprj.scan_controller.scan_data_in scan_controller_tb.uut.mprj.scan_controller.scan_data_out scan_controller_tb.uut.mprj.scan_controller.scan_latch_en @@ -46,13 +50,16 @@ scan_controller_tb.uut.mprj.scan_controller.active_select[8:0] scan_controller_tb.uut.mprj.scan_controller.inputs[7:0] scan_controller_tb.uut.mprj.scan_controller.outputs[7:0] -@28 -scan_controller_tb.uut.mprj.scan_controller.clk_divider.last_set -@22 -scan_controller_tb.uut.mprj.scan_controller.clk_divider.compare[7:0] -@8024 -scan_controller_tb.uut.mprj.scan_controller.clk_divider.counter[21:0] @20000 - +@28 +scan_controller_tb.uut.mprj.scan_wrapper_339501025136214612_0.data_in +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.slow_clk +>-4100000 +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.match +@22 +>0 +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.compare[7:0] +scan_controller_tb.uut.mprj.scan_controller.clk_divider_I.counter[7:0] [pattern_trace] 1 [pattern_trace] 0
diff --git a/verilog/dv/scan_controller_int/test_scan_controller.py b/verilog/dv/scan_controller_int/test_scan_controller.py index 1dcdf71..9c87630 100644 --- a/verilog/dv/scan_controller_int/test_scan_controller.py +++ b/verilog/dv/scan_controller_int/test_scan_controller.py
@@ -7,7 +7,7 @@ clock = Clock(dut.clk, 25, units="ns") # 40M cocotb.fork(clock.start()) - dut.driver_sel.value = 0b01 # internal + dut.driver_sel.value = 0b10 # internal dut.set_clk_div.value = 0 dut.inputs.value = 0 dut.active_sel.value = 0 @@ -43,13 +43,18 @@ assert dut.outputs.value == 0xAA # use the clock divider + await ClockCycles(dut.clk, 100) print("checking clock divider") + dut.RSTB.value = 0 await RisingEdge(dut.clk) dut.inputs.value = 1 dut.set_clk_div.value = 1 + await ClockCycles(dut.clk, 100) + dut.RSTB.value = 1 await ClockCycles(dut.clk, 2) dut.inputs.value = 0 + print("waiting for slow clock") # check slow clock output for i in range(2): await RisingEdge(dut.slow_clk)
diff --git a/verilog/dv/scan_controller_la/test_scan_controller.py b/verilog/dv/scan_controller_la/test_scan_controller.py index 9d7907e..6eadd7e 100644 --- a/verilog/dv/scan_controller_la/test_scan_controller.py +++ b/verilog/dv/scan_controller_la/test_scan_controller.py
@@ -7,7 +7,7 @@ clock = Clock(dut.clk, 25, units="ns") # 40M cocotb.fork(clock.start()) - dut.driver_sel.value = 0b10 # logic analyser + dut.driver_sel.value = 0b01 # logic analyser dut.RSTB.value = 0 dut.power1.value = 0 dut.power2.value = 0
diff --git a/verilog/rtl/scan_controller/properties.v b/verilog/rtl/scan_controller/properties.v index c104f6a..a14d115 100644 --- a/verilog/rtl/scan_controller/properties.v +++ b/verilog/rtl/scan_controller/properties.v
@@ -1,22 +1,24 @@ always @(*) begin if(driver_sel == 2'b00) begin // external driver - assert(outputs[0] == scan_data_in); - assert(scan_clk == ext_scan_clk); + assert(outputs[0] == scan_clk_in); + assert(outputs[1] == scan_data_in); + assert(scan_clk_out == inputs[0]); assert(scan_data_out == inputs[1]); assert(scan_select == inputs[2]); assert(scan_latch_en == inputs[3]); end else - if(driver_sel == 2'b10) begin // external driver + if(driver_sel == 2'b01) begin // la assert(la_scan_data_out == scan_data_in); - assert(scan_clk == la_scan_clk); + assert(scan_clk_out == la_scan_clk_in); assert(scan_data_out == la_scan_data_in); assert(scan_select == la_scan_select); assert(scan_latch_en == la_scan_latch_en); end else - if(driver_sel == 2'b01) begin // internal driver + if(driver_sel == 2'b10) begin // internal driver assert(int_scan_data_out == scan_data_out); - assert(scan_clk == int_scan_clk); + assert(scan_clk_out == int_scan_clk_out); assert(scan_data_in == int_scan_data_in); + assert(scan_clk_in == int_scan_clk_in); assert(scan_select == int_scan_select); assert(scan_latch_en == int_scan_latch_en); end
diff --git a/verilog/rtl/scan_controller/scan_controller.v b/verilog/rtl/scan_controller/scan_controller.v index 1591991..e1d75a8 100644 --- a/verilog/rtl/scan_controller/scan_controller.v +++ b/verilog/rtl/scan_controller/scan_controller.v
@@ -1,226 +1,451 @@ +`timescale 1ns / 100ps `default_nettype none -module scan_controller ( +module scan_controller #( + parameter integer NUM_DESIGNS = 8, + parameter integer NUM_IOS = 8, + + // auto-set + parameter integer PL = NUM_IOS - 1 +)( input wire clk, input wire reset, input wire [8:0] active_select, // which design is connected to the inputs and outputs - input wire [7:0] inputs, // inputs to the design (or external scan chain) + input wire [PL:0] inputs, // inputs to the design (or external scan chain) input wire set_clk_div, // set clock divider. See module below - output wire [7:0] outputs, // outputs from the design (or external scan chain) + output wire [PL:0] outputs, // outputs from the design (or external scan chain) output wire ready, // debug output that goes high once per refresh output wire slow_clk, // debug clock divider output - output wire scan_clk, // scan chain interface for the tiny designs, from perspective of this module - output wire scan_data_out, // see diagrams below for how the scan chain works + output reg scan_clk_out, // scan chain interface for the tiny designs, from perspective of this module + output reg scan_data_out, // see diagrams below for how the scan chain works + input wire scan_clk_in, // feedback clock after having done the whole round input wire scan_data_in, // will be driven by internal driver, external gpio pins, or Caravel logic analyser - output wire scan_select, // external scan chain driver muxes with ins/outs, eg microcontroller outside the ASIC - output wire scan_latch_en, + output reg scan_select, // external scan chain driver muxes with ins/outs, eg microcontroller outside the ASIC + output reg scan_latch_en, - input wire la_scan_clk, // logic analyser scan chain driver, driven by firmware running on Caravel's VexRisc - input wire la_scan_data_in, // signal names from perspective of this module + input wire la_scan_clk_in, // logic analyser scan chain driver, driven by firmware running on Caravel's VexRisc + input wire la_scan_data_in, // signal names from perspective of this module toward scan chain output wire la_scan_data_out, input wire la_scan_select, input wire la_scan_latch_en, - input wire [1:0] driver_sel, // 00 = external, 01 = internal, 10 = logic analyser + input wire [1:0] driver_sel, // 00 = external, 01 = logic analyzer, 1x = internal output wire [`MPRJ_IO_PADS-1:0] oeb // caravel harness needs output enable bar set low to enable outputs - ); +); + // Signals + // ------- + assign ready = active && state == ST_IDLE; + + // Reset + reg [2:0] rst_shift; + wire rst_i; + + // Muxing + // _in / _out are from the perspecitve of this module toward the scan + // chain. So all _in are FROM the scan chain (and either input to this + // module from there, or output from this module to LA / External) + + wire ext_scan_clk_in; + wire ext_scan_data_in; + wire ext_scan_clk_out; + wire ext_scan_data_out; + wire ext_scan_select; + wire ext_scan_latch_en; + + wire int_scan_clk_in; + wire int_scan_data_in; + reg int_scan_clk_out; + reg int_scan_data_out; + reg int_scan_select; + reg int_scan_latch_en; + + // FSM + localparam [3:0] + ST_IDLE = 0, // Idle + ST_IN_LOAD = 1, // Capture input + ST_IN_SHIFT_LO = 2, // Shift input to design: lo-clk + ST_IN_SHIFT_HI = 3, // Shift input to design: hi-clk + ST_IN_LATCH_WAIT = 4, // Wait before latching + ST_IN_LATCH = 5, // Latch + ST_OUT_LOAD_PRE = 6, // Prepare load + ST_OUT_LOAD = 7, // Clock once to load + ST_OUT_LOAD_POST = 8, // Wait for load to be done + ST_OUT_LOAD_CLR = 9, // Restore chain to shift mode + ST_OUT_SHIFT_LO = 10, // Shift output to us: lo-clk + ST_OUT_SHIFT_HI = 11, // Shift output to us: hi-clk + ST_OUT_CAP_WAIT = 12, // Wait for capture + ST_OUT_CAP = 13; // Capture to out local register + + reg active; + + reg [3:0] state; + reg [3:0] state_nxt; + + // Scan progress + reg [$clog2(NUM_IOS)-1:0] bit_cnt; + wire bit_last; + + reg [8:0] proj_cnt; + wire proj_sel; + wire proj_done; + + // Auto IO + reg [PL:0] aio_input_sync; + reg [PL:0] aio_input_reg; + reg [PL:0] aio_input_shift; + + reg [PL:0] aio_output_shift; + reg [PL:0] aio_output_reg; + + wire aio_input_sh; + wire aio_input_ld; + wire aio_output_cap; + + // Wait state config + reg [7:0] ws_cnt; + wire ws_cnt_done; + wire ws_cnt_run; + + // Wait state config + reg [2:0] ws_set_sync; + reg ws_set_now; + reg [7:0] ws_cfg; + + // Clock divider + wire slow_clk_ena; + + + // Misc + // ---- + + // Generate internal ties for the IO blocks assign oeb = {`MPRJ_IO_PADS{1'b0}}; - parameter NUM_DESIGNS = 8; - parameter NUM_IOS = 8; - - localparam START = 0; - localparam LOAD = 1; - localparam READ = 2; - localparam CAPTURE_STATE = 3; - localparam LATCH = 4; - - // scan chain muxing - // signal names in perspective of this module - wire ext_scan_clk = inputs[0]; - wire ext_scan_data_in = inputs[1]; - wire ext_scan_data_out = scan_data_in; - wire ext_scan_select = inputs[2]; - wire ext_scan_latch_en = inputs[3]; - - assign scan_clk = driver_sel == 2'b00 ? ext_scan_clk : driver_sel == 2'b01 ? int_scan_clk : la_scan_clk; - assign scan_data_out = driver_sel == 2'b00 ? ext_scan_data_in : driver_sel == 2'b01 ? int_scan_data_out : la_scan_data_in; - assign scan_select = driver_sel == 2'b00 ? ext_scan_select : driver_sel == 2'b01 ? int_scan_select : la_scan_select; - assign scan_latch_en = driver_sel == 2'b00 ? ext_scan_latch_en : driver_sel == 2'b01 ? int_scan_latch_en : la_scan_latch_en; - - wire int_scan_data_in = scan_data_in; - assign la_scan_data_out = scan_data_in; - assign outputs = driver_sel == 2'b01 ? outputs_r : {7'b0, ext_scan_data_out}; - `ifdef FORMAL `include "properties.v" `endif - // reg - reg [8:0] current_design; - reg [2:0] state; - reg [3:0] num_io; - reg scan_clk_r; - reg scan_select_out_r; + // Generate our own reset, with de-assertion + // synchronized to clock + always @(negedge clk or posedge reset) + if (reset) + rst_shift <= 3'b111; + else + rst_shift <= { rst_shift[1:0], 1'b0 }; - reg [7:0] inputs_r; - reg [7:0] outputs_r; - reg [7:0] output_buf; - - // wires - wire [8:0] active_select_rev = NUM_DESIGNS - 1 - active_select; - assign ready = state == START; - wire int_scan_latch_en = state == LATCH; - wire int_scan_clk = scan_clk_r; - wire int_scan_data_out = (state == LOAD && current_design == active_select_rev ) ? inputs_r[NUM_IOS-1-num_io] : 0; - wire int_scan_select = scan_select_out_r; - - // clock divider - clk_divider clk_divider ( - .clk (clk), - .set (set_clk_div), - .reset (reset), - .divider (inputs), - .slow_clk (slow_clk) - ); - wire [7:0] inputs_and_clk = set_clk_div ? { inputs[7:1], slow_clk } : inputs; - - /* - - LOAD - - ┌──┐ ┌──┐ ┌──┐ ┌──┐ - clk : ┘ └──┘ └──┘ └──┘ └────────────── - ┐ - scan en: └─────────────────────────────────── - ┐ ┌─────┐ - latch : └───────────────────────┘ └───── - ┐ ┌─────┐ ┌─────┐ - data o : └─────┘ └─────┘ └─────────── - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - data i : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + assign rst_i = rst_shift[2]; - READ + // Scan chain and IO muxing + // ------------------------ - ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ - clk : ┘ └──┘ └──┘ └──┘ └──┘ └──┘ └── - ┐ ┌─────┐ - scan en: └─────┘ └─────────────────────── - ┐ - latch : └─────────────────────────────────── - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - data o : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - ┐ ┌─────┐ ┌─────┐ - data i : └───────────┘ └─────┘ └───── + // To ensure we get something working, the scan chain can be driven + // three different ways. Automatic, from caravel LA and fully + // externally (see driver_sel input) - */ + // External interface + assign ext_scan_clk_out = inputs[0]; + assign ext_scan_data_out = inputs[1]; + assign ext_scan_select = inputs[2]; + assign ext_scan_latch_en = inputs[3]; - // FSM, only run it if driver_sel is set to internal - always @(posedge clk) begin - if(reset) begin - current_design <= 0; - state <= START; - inputs_r <= 0; - outputs_r <= 0; - scan_clk_r <= 0; - num_io <= 0; - output_buf <= 0; - end else if (driver_sel == 2'b01) begin - case(state) - START: begin - state <= LOAD; - inputs_r <= inputs_and_clk; - outputs_r <= output_buf; - current_design <= 0; - scan_select_out_r <= 0; - end + assign ext_scan_clk_in = scan_clk_in; + assign ext_scan_data_in = scan_data_in; - LOAD: begin - scan_clk_r <= ~scan_clk_r; - if(scan_clk_r) begin - num_io <= num_io + 1; + // Internal interface + assign int_scan_clk_in = scan_clk_in; + assign int_scan_data_in = scan_data_in; - if(num_io == NUM_IOS - 1) begin - num_io <= 0; - current_design <= current_design + 1; - - if(current_design == NUM_DESIGNS - 1) - state <= LATCH; - end + // LA interface + assign la_scan_data_out = scan_data_in; - end - - end - LATCH: begin - state <= READ; - current_design <= 0; - scan_select_out_r <= 1; - end - - READ: begin - scan_select_out_r <= 0; - scan_clk_r <= ~scan_clk_r; - if(scan_clk_r) begin - num_io <= num_io + 1; - if(current_design == active_select_rev) - output_buf[NUM_IOS-1-num_io] <= int_scan_data_in; - - if(num_io == NUM_IOS - 1) begin - num_io <= 0; - current_design <= current_design + 1; - - - if(current_design == NUM_DESIGNS - 1) begin - state <= START; - end - end - end - end - endcase - end - end - -endmodule - -module clk_divider ( - input clk, - input reset, - input set, - input [DIV_WIDTH-1:0] divider, - output slow_clk - ); - - // fastest useful clock period must be < max refresh rate: 750Hz - // 10M with 14bit divider (min) gives ~610Hz - // 10M with 22bit divider (max) gives ~2.4Hz - localparam MIN_WIDTH = 13; - localparam DIV_WIDTH = 8; - - reg [MIN_WIDTH+8:0] counter; - reg [DIV_WIDTH-1:0] compare; - reg last_set; - - assign slow_clk = counter[MIN_WIDTH + compare]; - - always @(posedge clk) begin - if(reset) begin - counter <= 0; - compare <= 0; - last_set <= 0; - end - else begin - // update divider on positive edge of set - if(set && !last_set) begin - compare <= divider; + // Mux toward scan-schain + always @(*) + begin + casez (driver_sel) + // External + 2'b00: begin + scan_clk_out = ext_scan_clk_out; + scan_data_out = ext_scan_data_out; + scan_select = ext_scan_select; + scan_latch_en = ext_scan_latch_en; end - counter <= counter + 1'b1; - last_set <= set; - end + + // Caravel LA + 2'b01: begin + scan_clk_out = la_scan_clk_in; + scan_data_out = la_scan_data_in; + scan_select = la_scan_select; + scan_latch_en = la_scan_latch_en; + end + + // Internal + 2'b1z: begin + scan_clk_out = int_scan_clk_out; + scan_data_out = int_scan_data_out; + scan_select = int_scan_select; + scan_latch_en = int_scan_latch_en; + end + endcase end -endmodule + // Synchronizer for inputs + always @(posedge clk) + begin + aio_input_sync <= inputs; + aio_input_reg <= driver_sel[1] ? aio_input_sync : 0; + end + + // Mux for outputs + assign outputs = driver_sel[1] ? aio_output_reg : {6'b0, ext_scan_data_in, ext_scan_clk_in}; + + + // FSM & control + // ------------- + + // Check if we're active + always @(posedge clk or posedge rst_i) + if (rst_i) + active <= 1'b0; + else + active <= driver_sel[1]; + + // State register + always @(posedge clk or posedge rst_i) + if (rst_i) + state <= ST_IDLE; + else + state <= state_nxt; + + // State transitions + always @(*) + begin + // Defaults + state_nxt = state; + + // Transitions + case (state) + ST_IDLE: + if (active) + state_nxt = ST_IN_LOAD; + + ST_IN_LOAD: + state_nxt = ST_IN_SHIFT_LO; + + ST_IN_SHIFT_LO: + state_nxt = ST_IN_SHIFT_HI; + + ST_IN_SHIFT_HI: + state_nxt = (proj_sel & bit_last) ? ST_IN_LATCH_WAIT : ST_IN_SHIFT_LO; + + ST_IN_LATCH_WAIT: + if (ws_cnt_done) + state_nxt = ST_IN_LATCH; + + ST_IN_LATCH: + state_nxt = ST_OUT_LOAD_PRE; + + ST_OUT_LOAD_PRE: + if (ws_cnt_done) + state_nxt = ST_OUT_LOAD; + + ST_OUT_LOAD: + state_nxt = ST_OUT_LOAD_POST; + + ST_OUT_LOAD_POST: + if (ws_cnt_done) + state_nxt = ST_OUT_LOAD_CLR; + + ST_OUT_LOAD_CLR: + if (ws_cnt_done) + state_nxt = ST_OUT_SHIFT_LO; + + ST_OUT_SHIFT_LO: + state_nxt = ST_OUT_SHIFT_HI; + + ST_OUT_SHIFT_HI: + state_nxt = (proj_done & bit_last) ? ST_OUT_CAP_WAIT : ST_OUT_SHIFT_LO; + + ST_OUT_CAP_WAIT: + if (ws_cnt_done) + state_nxt = ST_OUT_CAP; + + ST_OUT_CAP: + state_nxt = ST_IDLE; + endcase + end + + + // Scan progress tracking + // ---------------------- + + // Keep track of IO number + always @(posedge clk) + if (state == ST_IDLE) + bit_cnt <= 0; + else if ((state == ST_IN_SHIFT_HI) | (state == ST_OUT_SHIFT_HI)) + bit_cnt <= bit_last ? 0 : (bit_cnt + 1); + + assign bit_last = (bit_cnt == (NUM_IOS - 1)); + + // Keep track of project number + always @(posedge clk) + if (state == ST_IDLE) + proj_cnt <= 0; + else if (((state == ST_IN_SHIFT_HI) | (state == ST_OUT_SHIFT_HI)) & bit_last) + proj_cnt <= proj_cnt + 1; + + assign proj_sel = (proj_cnt == active_select); + assign proj_done = (proj_cnt == NUM_DESIGNS); + + + // Scan chain control + // ------------------ + + always @(posedge clk) + begin + int_scan_clk_out <= (state == ST_IN_SHIFT_HI) | (state == ST_OUT_LOAD) | (state == ST_OUT_SHIFT_HI); + int_scan_data_out <= aio_input_shift[PL]; + int_scan_select <= (state == ST_OUT_LOAD_PRE) | (state == ST_OUT_LOAD) | (state == ST_OUT_LOAD_POST); + int_scan_latch_en <= (state == ST_IN_LATCH); + end + + + // Shift registers + // --------------- + + // Local control from FSM + assign aio_input_sh = (state == ST_IN_SHIFT_HI); + assign aio_input_ld = (state == ST_IN_LOAD); + assign aio_output_cap = (state == ST_OUT_CAP); + + // Input + always @(posedge clk) + if (aio_input_ld) + aio_input_shift <= slow_clk_ena ? { aio_input_reg[PL-1:1], slow_clk } : aio_input_reg; + else if (aio_input_sh) + aio_input_shift <= { aio_input_shift[PL-1:0], 1'b0 }; + + // Output + always @(posedge int_scan_clk_in) + aio_output_shift <= { aio_output_shift[PL-1:0], int_scan_data_in }; + + always @(posedge clk) + if (aio_output_cap) + aio_output_reg <= aio_output_shift; + + + // Wait state counter + // ------------------ + + always @(posedge clk) + if (~ws_cnt_run | ws_cnt_done) + ws_cnt <= 0; + else + ws_cnt <= ws_cnt + 1; + + assign ws_cnt_done = (ws_cnt == ws_cfg); + + assign ws_cnt_run = ( + (state == ST_IN_LATCH_WAIT) | + (state == ST_OUT_LOAD_PRE) | + (state == ST_OUT_LOAD_POST) | + (state == ST_OUT_LOAD_CLR) | + (state == ST_OUT_CAP_WAIT) + ); + + + // Wait state config + // ----------------- + + // This dictates how many wait cycle we insert in various state + // of the load process. We have a sane default, but also allow + // override externally. + + always @(posedge clk or posedge rst_i) + if (rst_i) + ws_set_sync <= 3'b000; + else + ws_set_sync <= { ws_set_sync[1:0], (driver_sel == 2'b11) }; + + always @(posedge clk) + ws_set_now <= ~ws_set_sync[2] & ws_set_sync[1]; + + always @(posedge clk or posedge rst_i) + if (rst_i) + ws_cfg <= 8'd10; + else if (ws_set_now) + ws_cfg <= inputs; + + + // Clock Divider + // ------------- + + clk_divider clk_divider_I ( + .clk (clk), + .ce (aio_input_ld), + .set (set_clk_div), + .reset (rst_i), + .divider (inputs), + .active (slow_clk_ena), + .slow_clk (slow_clk) + ); + +endmodule // scan_controller + + +module clk_divider #( + parameter integer DIV_WIDTH = 8 +)( + input wire clk, + input wire ce, + input wire reset, + input wire set, + input wire [DIV_WIDTH-1:0] divider, + output wire active, + output reg slow_clk +); + + reg [DIV_WIDTH-1:0] counter; + reg [DIV_WIDTH-1:0] compare; + wire match; + + reg [2:0] set_sync; + reg set_now; + + // Detect rising edge (with synchronizer) + always @(posedge clk) + begin + set_sync <= { set_sync[1:0], set }; + set_now <= ~set_sync[2] & set_sync[1]; + end + + assign active = set_sync[2]; + + // Latch divider + always @(posedge clk) + if (set_now) + compare <= divider; + + // Compare + assign match = (counter == compare); + + // Counter + always @(posedge clk or posedge reset) + if (reset) + counter <= 0; + else if (ce) + counter <= match ? 0 : (counter + 1); + + // Clock gen + always @(posedge clk or posedge reset) + if (reset) + slow_clk <= 1'b0; + else if (ce) + slow_clk <= slow_clk ^ match; + +endmodule // clk_divider
diff --git a/verilog/rtl/user_project_wrapper.v b/verilog/rtl/user_project_wrapper.v index 7ea4afa..2079594 100644 --- a/verilog/rtl/user_project_wrapper.v +++ b/verilog/rtl/user_project_wrapper.v
@@ -96,13 +96,14 @@ .slow_clk (io_out[10]), .set_clk_div (io_in[11]), - .scan_clk (clk[0]), + .scan_clk_out (clk[0]), + .scan_clk_in (clk[NUM_MACROS]), .scan_data_out (data[0]), .scan_data_in (data[NUM_MACROS]), .scan_select (scan[0]), .scan_latch_en (latch[0]), - .la_scan_clk (la_data_in[0]), + .la_scan_clk_in (la_data_in[0]), .la_scan_data_in (la_data_in[1]), .la_scan_data_out (la_data_out[0]), .la_scan_select (la_data_in[2]),