Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ make MOD=sync_fifo # run a module's testbench
make wave MOD=sync_fifo # run the testbench and open the waveform in Surfer
make formal MOD=async_fifo # run the module's SymbiYosys proof
./synth_stats.sh sync_fifo # report a module's synthesis cost
./fmax.sh async_fifo tt_async_fifo wr_clk rd_clk # fmax and utilization
```

## Synthesis
Expand All @@ -99,6 +100,15 @@ Synthesized for the Digilent Basys 3 (Xilinx Artix-7).
| `sync_fifo` | 8 | 18 | 4 |
| `async_fifo` | 19 | 46 | 4 |

### Post-route timing

`fmax.sh` places and routes each FIFO in a registered-boundary harness and reports the maximum clock frequency. The frequencies come from the open nextpnr-xilinx flow, which is experimental and not vendor signed timing analysis. The `async_fifo` carries one frequency per clock domain, since a single figure has no meaning across the two independent clocks. The memory maps to distributed RAM rather than block RAM at this depth.

| Module | LUTs | Flip-flops | Block RAMs | Distributed RAM | Fmax |
|--------|------|------------|------------|-----------------|------|
| `sync_fifo` | 8 | 18 | 0 | 2 | 324 MHz |
| `async_fifo` | 19 | 46 | 0 | 2 | 296 MHz write, 343 MHz read |

### Tool versions

Icarus Verilog 13.0, Yosys 0.66, SymbiYosys 0.66 with Z3, and Surfer.
Icarus Verilog 13.0, Yosys 0.66, SymbiYosys 0.66 with Z3, nextpnr-xilinx 0.8.2, and Surfer.
53 changes: 53 additions & 0 deletions fmax.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Fmax and utilization
# Usage ./fmax.sh <module> <harness> <clock> [clock]
set -e

MOD="$1"; HARNESS="$2"; shift 2
CLKS="$*"
CHIPDB="$HOME/Documents/code/nextpnr-xilinx/xilinx/xc7a35t.bin"
mkdir -p build

# Module utilization
sv2v rtl/*.sv > build/util_$MOD.v
yosys -p "read_verilog build/util_$MOD.v; synth_xilinx -top $MOD -flatten; stat" 2>/dev/null \
| awk '
/^=== / { lut = 0; ff = 0; bram = 0; dram = 0 }
/^[[:space:]]+[0-9]+[[:space:]]+LUT/ { lut += $1 }
/^[[:space:]]+[0-9]+[[:space:]]+FD/ { ff += $1 }
/^[[:space:]]+[0-9]+[[:space:]]+RAMB/ { bram += $1 }
/^[[:space:]]+[0-9]+[[:space:]]+RAM[0-9]/ { dram += $1 }
END { printf "util %d %d %d %d\n", lut+0, ff+0, bram+0, dram+0 }' > build/util_$MOD.txt

# Route the harness
sv2v rtl/*.sv fmax/$HARNESS.sv > build/fmax_$HARNESS.v
yosys -q -p "read_verilog build/fmax_$HARNESS.v; synth_xilinx -top $HARNESS -flatten; write_json build/fmax_$HARNESS.json" 2>/dev/null

# Pin pool
CLKPINS=(W5 V17 V16)
IOPINS=(W16 W17 W15 V15 W14 W13 U16 E19 U19 V19)
XDC=build/fmax_$HARNESS.xdc
: > $XDC
ci=0; ii=0
for p in $(python3 - "build/fmax_$HARNESS.json" "$HARNESS" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
ports = d["modules"][sys.argv[2]]["ports"]
print(" ".join(ports.keys()))
PY
); do
if echo " $CLKS " | grep -q " $p "; then pin=${CLKPINS[$ci]}; ci=$((ci+1)); else pin=${IOPINS[$ii]}; ii=$((ii+1)); fi
echo "set_property PACKAGE_PIN $pin [get_ports $p]" >> $XDC
echo "set_property IOSTANDARD LVCMOS33 [get_ports $p]" >> $XDC
done

nextpnr-xilinx --chipdb "$CHIPDB" --xdc $XDC --json build/fmax_$HARNESS.json \
--fasm build/fmax_$HARNESS.fasm --router router2 2>build/fmax_$HARNESS.log || true

read lut ff bram dram < <(awk '{print $2, $3, $4, $5}' build/util_$MOD.txt)
echo "=== $MOD ==="
echo " LUTs $lut Flip-flops $ff Block RAMs $bram Distributed RAM $dram"
# Fmax per clock
grep -iE "Max frequency for clock" build/fmax_$HARNESS.log \
| sed -E "s/.*clock '([^']+)': ([0-9.]+) MHz.*/\1 \2/" \
| awk '{ f[$1] = $2 } END { for (c in f) printf " Fmax %-14s %s MHz\n", c, f[c] }'
36 changes: 36 additions & 0 deletions fmax/tt_async_fifo.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Async FIFO harness
module tt_async_fifo (
input logic wr_clk,
input logic rd_clk,
input logic rst_n,
input logic si,
output logic so_w,
output logic so_r
);
localparam int NW = 9;

logic [NW-1:0] win_r;
logic rin_r;
logic full_w;
logic [7:0] rd_data_w;
logic empty_w;

always_ff @(posedge wr_clk) win_r <= {win_r[NW-2:0], si};
always_ff @(posedge rd_clk) rin_r <= si;

async_fifo dut (
.wr_clk (wr_clk),
.wr_rst_n(rst_n),
.wr_en (win_r[0]),
.wr_data (win_r[8:1]),
.full (full_w),
.rd_clk (rd_clk),
.rd_rst_n(rst_n),
.rd_en (rin_r),
.rd_data (rd_data_w),
.empty (empty_w)
);

always_ff @(posedge wr_clk) so_w <= full_w;
always_ff @(posedge rd_clk) so_r <= ^{rd_data_w, empty_w};
endmodule
29 changes: 29 additions & 0 deletions fmax/tt_sync_fifo.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Sync FIFO harness
module tt_sync_fifo (
input logic clk,
input logic rst_n,
input logic si,
output logic so
);
localparam int NIN = 10;
localparam int NOUT = 10;

logic [NIN-1:0] in_r;
logic [NOUT-1:0] out_w, out_r;

always_ff @(posedge clk) in_r <= {in_r[NIN-2:0], si};

sync_fifo dut (
.clk (clk),
.rst_n (rst_n),
.wr_en (in_r[0]),
.rd_en (in_r[1]),
.wr_data(in_r[9:2]),
.rd_data(out_w[7:0]),
.full (out_w[8]),
.empty (out_w[9])
);

always_ff @(posedge clk) out_r <= out_w;
always_ff @(posedge clk) so <= ^out_r;
endmodule
Loading