diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..cf33752 --- /dev/null +++ b/build.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Build script for hsdaoh FPGA projects +# Usage: ./build.sh +# e.g.: ./build.sh hsdaoh_nano20k_test.gprj + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Auto-detect Gowin installation +GOWIN_DIR="${GOWIN_DIR:-$HOME/tools/Gowin_V1.9.11.03_Education_Linux/IDE}" + +if [ ! -f "$GOWIN_DIR/bin/gw_sh" ]; then + echo "ERROR: gw_sh not found at $GOWIN_DIR/bin/gw_sh" + echo "Set GOWIN_DIR to your Gowin IDE directory" + exit 1 +fi + +export LD_LIBRARY_PATH="$GOWIN_DIR/lib/:$LD_LIBRARY_PATH" +export QT_QPA_PLATFORM=offscreen + +echo "Using Gowin IDE: $GOWIN_DIR" +exec "$GOWIN_DIR/bin/gw_sh" "$SCRIPT_DIR/build.tcl" "$@" diff --git a/build.tcl b/build.tcl new file mode 100644 index 0000000..c4c1450 --- /dev/null +++ b/build.tcl @@ -0,0 +1,37 @@ +# build.tcl - Command-line build script for hsdaoh FPGA projects +# Usage: gw_sh build.tcl +# e.g.: gw_sh build.tcl hsdaoh_nano20k_test.gprj + +if {$argc < 1} { + puts "Usage: gw_sh build.tcl " + exit 1 +} + +set project_file [lindex $argv 0] + +if {![file exists $project_file]} { + puts "ERROR: Project file '$project_file' not found" + exit 1 +} + +puts "============================================" +puts "Building: $project_file" +puts "============================================" + +open_project $project_file + +# Enable timing-driven PnR and generate text timing report +set_option -timing_driven 1 +set_option -gen_text_timing_rpt 1 +set_option -show_all_warn 1 +set_option -print_all_synthesis_warning 1 + +# Increase PnR effort for better timing closure +set_option -place_option 1 +set_option -route_option 1 + +run all + +puts "============================================" +puts "Build complete!" +puts "============================================" diff --git a/common/async_fifo/rptr_empty.v b/common/async_fifo/rptr_empty.v index c7dc3bb..4a23ec1 100644 --- a/common/async_fifo/rptr_empty.v +++ b/common/async_fifo/rptr_empty.v @@ -2,6 +2,10 @@ // distributed under the mit license // https://opensource.org/licenses/mit-license.php +// Optimized: replaced chained adder (rbin+inc then +1) with parallel +// pre-computation of rbin+1 and rbin+2, selected via mux. This reduces +// the arempty critical path from ~9 to ~5-6 logic levels. + `timescale 1 ns / 1 ps `default_nettype none @@ -21,9 +25,28 @@ module rptr_empty ); reg [ADDRSIZE:0] rbin; - wire [ADDRSIZE:0] rgraynext, rbinnext, rgraynextm1; + wire [ADDRSIZE:0] rgraynext, rbinnext; wire arempty_val, rempty_val; + // Pre-compute incremented values from registered rbin (parallel, not chained) + wire [ADDRSIZE:0] rbin_p1 = rbin + 1'b1; + wire [ADDRSIZE:0] rbin_p2 = rbin + 2'd2; + wire do_read = rinc & ~rempty; + + // Gray code conversions from pre-computed values + wire [ADDRSIZE:0] rgray_p1 = (rbin_p1 >> 1) ^ rbin_p1; + wire [ADDRSIZE:0] rgray_p2 = (rbin_p2 >> 1) ^ rbin_p2; + + // Select based on whether a read is happening this cycle + // When do_read: rbinnext = rbin+1, rgraynext = gray(rbin+1) + // When !do_read: rbinnext = rbin, rgraynext = gray(rbin) = rptr (already registered) + assign rbinnext = do_read ? rbin_p1 : rbin; + assign rgraynext = do_read ? rgray_p1 : rptr; + + // "Almost empty" = will be empty after one more read from projected next position + // rgraynextm1 = gray(rbinnext + 1) = gray(do_read ? rbin+2 : rbin+1) + wire [ADDRSIZE:0] rgraynextm1 = do_read ? rgray_p2 : rgray_p1; + //------------------- // GRAYSTYLE2 pointer //------------------- @@ -37,10 +60,7 @@ module rptr_empty end // Memory read-address pointer (okay to use binary to address memory) - assign raddr = rbin[ADDRSIZE-1:0]; - assign rbinnext = rbin + (rinc & ~rempty); - assign rgraynext = (rbinnext >> 1) ^ rbinnext; - assign rgraynextm1 = ((rbinnext + 1'b1) >> 1) ^ (rbinnext + 1'b1); + assign raddr = rbin[ADDRSIZE-1:0]; //--------------------------------------------------------------- // FIFO empty when the next rptr == synchronized wptr or on reset diff --git a/common/async_fifo/wptr_full.v b/common/async_fifo/wptr_full.v index 2462a19..1697f79 100644 --- a/common/async_fifo/wptr_full.v +++ b/common/async_fifo/wptr_full.v @@ -2,6 +2,10 @@ // distributed under the mit license // https://opensource.org/licenses/mit-license.php +// Optimized: replaced chained adder (wbin+inc then +1) with parallel +// pre-computation of wbin+1 and wbin+2, selected via mux. Same approach +// as rptr_empty optimization. + `timescale 1 ns / 1 ps `default_nettype none @@ -21,9 +25,25 @@ module wptr_full ); reg [ADDRSIZE:0] wbin; - wire [ADDRSIZE:0] wgraynext, wbinnext, wgraynextp1; + wire [ADDRSIZE:0] wgraynext, wbinnext; wire awfull_val, wfull_val; + // Pre-compute incremented values from registered wbin (parallel, not chained) + wire [ADDRSIZE:0] wbin_p1 = wbin + 1'b1; + wire [ADDRSIZE:0] wbin_p2 = wbin + 2'd2; + wire do_write = winc & ~wfull; + + // Gray code conversions from pre-computed values + wire [ADDRSIZE:0] wgray_p1 = (wbin_p1 >> 1) ^ wbin_p1; + wire [ADDRSIZE:0] wgray_p2 = (wbin_p2 >> 1) ^ wbin_p2; + + // Select based on whether a write is happening this cycle + assign wbinnext = do_write ? wbin_p1 : wbin; + assign wgraynext = do_write ? wgray_p1 : wptr; // wptr = gray(wbin) + + // "Almost full" look-ahead + wire [ADDRSIZE:0] wgraynextp1 = do_write ? wgray_p2 : wgray_p1; + // GRAYSTYLE2 pointer always @(posedge wclk or negedge wrst_n) begin @@ -36,16 +56,6 @@ module wptr_full // Memory write-address pointer (okay to use binary to address memory) assign waddr = wbin[ADDRSIZE-1:0]; - assign wbinnext = wbin + (winc & ~wfull); - assign wgraynext = (wbinnext >> 1) ^ wbinnext; - assign wgraynextp1 = ((wbinnext + 1'b1) >> 1) ^ (wbinnext + 1'b1); - - //------------------------------------------------------------------ - // Simplified version of the three necessary full-tests: - // assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) && - // (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) && - // (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0])); - //------------------------------------------------------------------ assign wfull_val = (wgraynext == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); assign awfull_val = (wgraynextp1 == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]});