[video member=pervognsen stream_platform=twitch project=bitwise title="Sequential Logic, Part 2" vod_platform=youtube id=kU9QIqdy5i0 annotator=Miblo] [0:08][Recap and set the stage for the day continuing with :"sequential logic"][:speech] [0:46][Review the off-by-one issue from yesterday, in which simulate_test() failed to update the module output values after a tick and before calling in to the tester][:"sequential logic" :research] [2:18][On splitting the update logic into two parts][:"sequential logic" :research] [3:39][Consult the emitted code of our entire circuit, on the update logic split between update() - computing outputs - and tick() - computing register values][:"sequential logic" :run] [7:52][Note the fewer yields in example39_test()][:"sequential logic" :research] [8:50][:Run simulate_test() successfully on example39_test][:emulation :"sequential logic"] [9:03][Combinational pipelining][:"sequential logic" :speech] [9:46][Introduce delay() to construct feed-forward register chains][:"sequential logic"] [13:52][Define Example40 module as a delay][:"sequential logic"] [15:18][Check the graph of Example40, the delay][:"debug visualisation" :"sequential logic" :run] [15:54][Create simulation test for Example40][:emulation :"sequential logic"] [16:46][:Run simulate_test() successfully on example40_test][:emulation :"sequential logic"] [17:50][A few words on the utility of delay like an operator][:"sequential logic" :speech] [21:49][Augmenting delay analysis to compute critical paths between internal registers][:profiling :"sequential logic" :speech] [23:18][Change the DelayAnalyzer use nodes rather than node names][:profiling :"sequential logic"] [29:45][:Run it to see our delay analysis][:profiling :"sequential logic"] [30:49][Introduce combinational_multiplier() as a dumb multiplier][:"sequential logic"] [34:19][:Run simulate_test() on the combinational_multiplier() successfully][:emulation :"sequential logic"] [34:26][Introduce pipelined_multiplier() interspersing delay() in the combinational_multiplier(), also adding an "enable" control signal in Example42][:"sequential logic"] [41:56][:Run it and hit runtime error "dictionary changed size during iteration"][:emulation :"sequential logic"] [42:10][Enable linearize() to handle register discovery][:"code generation"] [44:08][:Run it successfully][:emulation :"sequential logic"] [44:13][Create a test of Example42 without pipelining][:"code generation" :emulation :"sequential logic"] [45:00][:Run it, hit an AttributeError, and investigate the problem in register discovery][:"code generation" :emulation :"sequential logic"] [50:45][Investigate the project in the combinational_multiplier()][:emulation :"sequential logic"] [51:39][Pass through the __iter__() of SimulatorInstance while debugging][:"sequential logic"] [52:06][Step through example42_test() inspecting the values][:emulation :"sequential logic" :run] [53:28][Fix the "enable" control signal in Example42 to be a single bit][:"sequential logic"] [53:43][:Run simulate_test() on example42_test to completion][:emulation :"sequential logic"] [54:22][Change pipelined_multiplier() to use the current values of x and y before reassigning them][:"sequential logic"] [56:00][:Run simulate_test() on example42_test, hit our test assertion and step in to pipelined_multiplier() to investigate][:emulation :"sequential logic"] [58:05][Check out our generated code to see self.r8 being set incorrectly][:"sequential logic" :run] [59:47][Change Example42 to set p and p_valid to their output() separately from the pipelined_multiplier() call][:"sequential logic"] [1:00:06][:Run it to see the same p_valid value as before, and check the code][:"sequential logic" :run] [1:01:51][Scrutinise linearize() to discover the issue][:"code generation" :research] [1:02:42][Prevent name collision during register discovery in linearize()][:"code generation"] [1:06:43][Realise that both update() and tick() need to use the same register names, and consider how to resolve our failure][:"code generation" :speech] [1:07:28][Make linearize() use a shared dictionary for registers][:"code generation"] [1:08:27][:Run it still unsuccessfully, with the same p_valid() value as before][:"code generation"] [1:08:41][Make linearize() clone the counter as well][:"code generation"] [1:09:01][:Run it successfully][:emulation :"code generation"] [1:09:17][Try to clean up the update_instructions and tick_instructions split in linearize()][:"code generation"] [1:10:55][:Run it and hit an AttributeError][:"code generation"] [1:12:20][Revert linearize() to the working version][:"code generation"] [1:12:56][:Run simulate_test() on the real example42_test unsuccessfully, and inspect the computed values][:emulation :"sequential logic"] [1:16:56][Reformulate combinational_multiplier() as a parallel assignment][:"sequential logic"] [1:17:51][:Run it to see that that works][:emulation :"sequential logic"] [1:17:55][Change pipelined_multiplier() to use this new parallel formulation of combinational_multiplier()][:"sequential logic"] [1:18:54][Enable delay() to handle tuples][:"sequential logic"] [1:19:25][:Run it to see it still doesn't work][:"sequential logic"] [1:19:28][Fix pipelined_multiplier() to iterate over all the bits][:"sequential logic"] [1:19:33][:Run the pipelined multiplier successfully][:"sequential logic"] [1:20:21][Introduce example42_test_producer() and example42_test_consumer() to give us two different agents to feed and consume from the multiplier][:"sequential logic"] [1:22:04][Enable simulate_test() to handle multiple testers][:emulation] [1:24:37][:Run it to ensure that the existing tests do run][:emulation] [1:24:55][Write a simulation test of our multiplier producer and consumer running in parallel][:emulation :"sequential logic"] [1:25:23][:Run simulate_test() on our multiplier producer and consumer, fail the test and investigate why][:emulation :"sequential logic"] [1:28:43][Fix example42_test_consumer() to correctly yield][:"sequential logic"] [1:29:17][:Run simulate_test() on our multiplier with parallel producer and consumer successfully][:emulation :"sequential logic"] [1:29:25][@spriithy][New place?] [1:29:45][Reflect on the utility of running more than one test co-routine at once][:emulation :"sequential logic" :speech] [1:30:10][Further decouple the producer and consumer using a separate "expected" queue[ref site="Python 3.7.0 documentation" page="17.7. queue — A synchronized queue class" url=https://docs.python.org/3/library/queue.html]][:"sequential logic"] [1:33:23][:Run it to see that example42_test_consumer() never gets to run, and investigate why][:emulation :"sequential logic"] [1:35:30][:Run it again to see that example42_test_consumer() now gets to run][:emulation :"sequential logic"] [1:36:32][Revert the decoupling][:"sequential logic"] [1:36:57][:Run it successfully][:"sequential logic"] [1:37:17][Reflect on our pipelined system, impressing the need to keep thing in phase when mixing, and the utility of pipelining to improve the throughput of any combinational circuit][:"sequential logic" :speech] [1:43:36][Pipelining a 1024-bit carry-propagate adder][:"sequential logic" :speech] [1:51:37][That's it for the main stream][:speech] [/video]