Your task is to design, lay out, and verify an asynchronous fifo. This fifo should:
- have asynchronous reset
- have seperate read and write clocks and clock domains
- use a 2-flip flop synchronizer to cross clock domains
- use gray encoding when feeding multi-bit signals across a synchronizer
- write enable and write data inputs, write full output
- read enable and read data inputs, read empty output
- parameterized fifo width (width of read/write data)
- optional: parameterized fifo depth
The internet is full of asynchronous fifo implementations. The goal here is that you write your own so that you understand how they operate. Here is a good starting point:
For this lab, we will learn to use cocotb as a powerful way to do verification. A template test has already been provided for you in the cocotests directory. Use this template to verify your async fifo.
CocoTB needs to know what your top level module is, and where to look for verilog files. This configuration is done by editing the Makefile in the teste directory.
- Set
COCOTB_TOPLEVELto the name of your top level module - Set
COCOTB_TEST_MODULESto the name of your python test file VERILOG_SOURCESshould get auto configured, but if necessary set it to a list of paths to your verilog files.VERILOG_INCLUDE_DIRScan be used to add more directories where include files (.vh or .svh) are located
To run your cocotb tests, use the following options:
- Run
make cocotestsin lab4 folder to run all cocotb tests. cdinto a specific test and runmakein that folder to run that specific tests
This will create a waveform file called dump.fst or dump.vcd in the test folder.
Your testbench will likely want more than one concurrent function driving your DUT. This could be used to indepdently drive the read and write ports of your async fifo.
async def function1 (dut):
pass
async def function2 (dut):
pass
@cocotb.test()
async def test(dut):
task1 = cocotb.start_soon(function1(dut))
task2 = cocotb.start_soon(function2(dut))
await task1
await task2Moving from one clock to two clocks, we need to add both clocks to the openlane configuration using the CLOCK_PORT variable.
| Json | Yaml |
{
"CLOCK_PORT": ["clk_r", "clk_w"]
} |
---
CLOCK_PORT:
- clk_r
- clk_w
|
When building our design in Openlane with one clock, we can set the period without defining design constraints. However, with more clocks we have to manually define the timing constraints using an SDC file.
- Create a file called
constraints.sdc - Set that file as the SDC file for PNR and Signoff
| Json | Yaml |
{
"PNR_SDC_FILE": "constraints.sdc",
"SIGNOFF_SDC_FILE": "constraints.sdc"
} |
---
PNR_SDC_FILE: constraints.sdc
SIGNOFF_SDC_FILE: constraints.sdc
|
Configure the design constraints to set the period of each clock. A sample is shown below. The shown periods are arbitrary. Make sure clocks are set to asynchronous so they are not assumed to align.
puts "\[INFO\]: Creating Clocks"
create_clock [get_ports clk_r] -name clk_r -period 7
set_propagated_clock clk_r
create_clock [get_ports clk_w] -name clk_w -period 14
set_propagated_clock clk_w
set_clock_groups -asynchronous -group [get_clocks {clk_r clk_w}]
We need to prevent the tools from messing up the validity of the gray-encoded clock domain crossing. One way it could mess up is to make the propagation delay of one of the bits way too high. To do this, we constrain its max delay.

- To constrain max delay, we need to know about specific flip flops in our design
- Our design must already have synthesized to see the flip flops
- Use
openlane config.json --flow Classic -T yosys.synthesisto run openlane only through the sythesis step- Make sure the
SYNTH_AUTONAMEflag is set to true in your configuration
- Make sure the
- View the netlist file (.nl.v) in the
final/nldirectory of your most recent run to see the names of synthesized flops - Copy and Modify the following constraints:
puts "\[INFO\]: Setting Max Delay"
set read_period [get_property -object_type clock [get_clocks {clk_r}] period]
set write_period [get_property -object_type clock [get_clocks {clk_w}] period]
set min_period [expr {min(${read_period}, ${write_period})}]
set_max_delay -from [get_pins r_gray.out*df*/CLK] -to [get_pins rcdc.r1*df*/D] $min_period
set_max_delay -from [get_pins w_gray.out*df*/CLK] -to [get_pins wcdc.r1*df*/D] $min_period
- Replace
r_gray.outwith name of the flip flop that holds gray encoded pointer, before crossing clock domains - Replace
rcdc.r1with the name of the flip flop on the other clock domain that receives this pointer - Same idea for
w_gray.outandwcdc.r1
- Async Fifo RTL Design
- CocoTB Test Bench, making sure to test:
- both read and write domains active at the same time
- read and write domains independently filling/emptying fifo
- Timing Results from Successful Openlane Run