generateCRAFT cycle · 2.5 hours · Wed 6/3 · Eve: Cooking workshop
HDL for Digital System Design · UCF ECE · Barcelona Summer 2026
| Phase | Time | Activity |
|---|---|---|
| 🌍 Contextualize | 10 min | IP libraries · Semidynamics' parameterized RISC-V cores |
| ⚠️ Reframe | 15 min | generate ≠ runtime loop · elaboration-time hardware |
| 🛠 Assemble | 70 min | Param N-bit counter · AI-assisted param TB · generate driver · hierarchical top |
| 🛡 Fortify | 45 min | Sweep WIDTH=4/8/16/32 · yosys stat PPA table · hardware verify |
| 🔗 Transfer | 10 min | Cooking workshop metaphor · D9 preview |
yosys stat table. This is what runs to Day 14.
▸ Phase 1 of 5 · ~10 min
Companies ship parameter families, not single modules
generate if.Parameters turn a module into a product line. One verified DUT covers a thousand SKUs.
Today you build your first parameter family. By Week 4 your whole project is parameterized.
▸ Phase 2 of 5 · ~15 min
When the loop runs matters more than what it does
generate Is Not a Runtime Loop"generate for (i=0; i<N; i++) is like a software for — it processes 4 things one at a time, fast."
generate for unrolls at synthesis time. N=4 creates four independent physical instances that all run in parallel on the same clock edge. The loop doesn't exist in the netlist.
generate Unrolls Into HardwareN copies of the body exist simultaneously in silicon.
module counter #(
parameter WIDTH = 8
)(
input wire clk, rst, en,
output reg [WIDTH-1:0] q,
output wire rollover
);
assign rollover = (q == {WIDTH{1'b1}}) & en;
always @(posedge clk) begin
if (rst) q <= 0;
else if (en) q <= q + 1'b1;
end
endmodule
// Instantiate three sizes from one module:
counter #(.WIDTH(4)) c4 (.clk(clk), .rst(rst), .en(en), .q(q4), .rollover(r4));
counter #(.WIDTH(8)) c8 (.clk(clk), .rst(rst), .en(en), .q(q8), .rollover(r8));
counter #(.WIDTH(16)) c16 (.clk(clk), .rst(rst), .en(en), .q(q16), .rollover(r16));
One module · three width-specific netlists. The synthesizer specializes each one.
generate for & generate if// generate for: replicate hardware
genvar i;
generate
for (i = 0; i < NUM_LEDS; i = i + 1) begin : g_blink
blinker #(.PERIOD(1_000_000 * (i+1))) b
(.clk(clk), .led(leds[i]));
end
endgenerate
// generate if: include features conditionally
generate
if (HAS_PARITY) begin : g_parity
wire par = ^data; // XOR-reduce
assign tx = {par, data};
end else begin : g_no_parity
assign tx = data;
end
endgenerate
: g_blink) keeps the hierarchy navigable in yosys show and GTKWave.
▸ Phase 3 of 5 · ~70 min · You build · across widths
One module · four widths · one top
2^WIDTH - 1. Critique & fix.generate for LED driver — 4 blinkers, period scales with index.Write a self-checking Icarus-compatible testbench for this counter module:
module counter #(parameter WIDTH=8) (
input clk, rst, en, output [WIDTH-1:0] q, output rollover);
Requirements:
- Test WIDTH = 4, 8, and 16 in the SAME testbench using #(.WIDTH(N)) overrides
- For each instance, verify rollover fires at q == (2**WIDTH - 1)
- Verify reset clears q to 0 from any value
- Verify en=0 freezes q
- Use !== for comparisons, print PASS/FAIL summary, $finish at end
- Target IEEE 1364-2005 Verilog (no SystemVerilog)
2**WIDTH - 1 per instance, or hardcode 255? Hardcoding is the #1 failure mode.module lab_instrument (
input wire clk,
input wire btn_async,
output wire [6:0] seg
);
wire btn_clean, tick;
wire [3:0] count;
debouncer #(.THRESHOLD(250_000)) u_db (.clk(clk), .in_async(btn_async),
.out_clean(btn_clean));
counter #(.WIDTH(4)) u_cnt (.clk(clk), .rst(1'b0), .en(btn_clean),
.q(count), .rollover());
hex_to_7seg u_disp (.hex(count), .seg(seg));
endmodule
Three modules. Three days of work. Composed in 10 lines. This is the payoff for hierarchy.
▸ Phase 4 of 5 · ~45 min · Verify · measure PPA · harden
Make the synthesizer show you the cost
for W in 4 8 16 32; do
yosys -p "read_verilog -DWIDTH=$W counter.v; \
synth_ice40 -top counter; stat" \
2>&1 | grep -E "SB_LUT4|SB_DFF"
done
Fill in the table together on the projector:
| WIDTH | SB_LUT4 | SB_DFF | Predicted |
|---|---|---|---|
| 4 | ? | ? | ~4 LUTs, 4 FFs |
| 8 | ? | ? | ~8 LUTs, 8 FFs |
| 16 | ? | ? | ~16 LUTs, 16 FFs |
| 32 | ? | ? | ~32 LUTs, 32 FFs |
More flip-flops ⇒ more switching ⇒ more dynamic power. (We'll quantify Day 10.)
Wider counter ⇒ longer carry chain ⇒ lower Fmax. (Day 10 measures this.)
Today's table is your first Area data point. Keep it for the project report.
D3 seeded it · D8 formalizes the habit · D10 adds timing · D14 uses it to defend project choices.
generate for blinkers: all 4 LEDs blink at different rates from one module instantiation.▸ Phase 5 of 5 · ~10 min
Take it to the kitchen tonight
for loopgenerate forThu 6/4 is a catch-up day — finish this week's labs and start scoping a project. Project selection is due Tue 6/9 (Week 3, D10 day). Fri 6/5 is free.
Week 3 opens with Memory (D9, Mon 6/8): ROM, RAM, and Block RAM inference. The iCE40's 16 EBR blocks are free, fast memory — but only if your code matches the inference pattern. You'll build a ROM-driven LED sequencer and a RAM with read-after-write verification.
🔗 End of Day 8 · A la cocina!
You can now build a parameter family and read its PPA cost from the synthesizer.
After the catch-up day and weekend, Week 3 opens with where the data lives.