Quick question that I am likely missing an obvious solution for. I have a relatively simple Verilog design which I'll call taco, where the top-level design entity is taco_top (because I'm writing this before lunch).
It's the only module in the design with all relevant logic contained in taco_top. The design targets the ever-popular Lattice iCE40 UltraPlus FPGA. In taco_top, I'm using the iCE40's low-frequency and high-frequency oscillators to generate 10kHz and 6MHz clocks. There are no external clocks in the design; taco_top's ports all correspond to physical I/O pins on the FPGA and the clocks I need are sourced from these internal oscillators.
In simulation, I want to replace these with simple dummy clocks with the appropriate period since I can't simulate the LFOSC and HFOSC primitives. So, I've created the appropriate simulation only clocks and kludged them into my design with `define which is not particularly elegant.
In writing a testbench, I would normally instantiate my taco_top module and feed it the appropriate stimuli, but those sim clocks are buried inside / not exposed at the top-level.
What's an elegant way to structure my design such that I can easily feed those clocks in simulation and maintain their behavior in the synthesized design?
1 Answer 1
In our designs, we normally divide the design into two layers, like an onion. We call the outer layer the "pad ring" (e.g., taco_io
), and it contains everything that's specific to the physical interfaces of the chip — I/O buffers, on-chip clocks, etc.
The inner layer is the "core" layer, and contains all of the actual application functionality. In other words, the functional hierarchy is implemented with taco_core
as its top level. The functional simulation testbench instantiates taco_core
only, where it has access to drive those clocks you're talking about.
Your taco_top
is used for synthesis only. It just instantiates two modules: taco_io
and taco_core
and connects them together. It also connects taco_io
to the actual external pins of the device.
Note that you could put all of those I/O bits right into taco_top
, but we find that it's generally cleaner to put them in their own submodule (taco_io
).