diff --git a/README.md b/README.md index 2bb387c..d36b3c8 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,22 @@ The project implements a simple message processing pipeline that parses fixed-wi ```mermaid flowchart LR - A(input) --> |32-bit input message| B(message parser) - B --> |message type| C(message filter) - B --> |values| C - - B --> |values| D(Output Formatter) - C --> |accepted/rejected| D - D --> |values| E(output) - D --> |reject reason| E + A("32-bit message
input_valid") --> B["input register
(clk, reset)"] + B --> C["parser / filter / formatter"] + C --> D["output register
(clk, reset)"] + D --> E("value_a / value_b
reject_reason
output_valid") ``` +For flowchart with module-level detail, see [`rtl/README.md`](rtl/README.md). + ## Current Features -- 32-bit message parser -- Simple parameterised message filter -- Output formatter for accepted/rejected messages -- Top-level RTL pipeline -- Original Verilog testbench -- `cocotb` tests using fixed input cases -- Python reference model for checking expected behaviour +- Synchronous Verilog message-processing pipeline +- 32-bit parser, parameterised filter, and output formatter +- `cocotb` verification with fixed and randomised test cases +- Python reference model for expected behaviour +- GitHub Actions CI for automated tests +- Vivado synthesis and timing reports ## Message Format @@ -42,6 +39,8 @@ flowchart LR The project currently includes fixed-case and randomised cocotb tests. The cocotb randomised tests run automatically using GitHub Actions. +For cocotb setup and test-running instructions, see [`tb/cocotb/README.md`](tb/cocotb/README.md). + | File | Purpose | | -------------------------- | ------------------------------------------------------------- | | `test_direct_cases.py` | Fixed input cases with manually specified expected outputs | @@ -49,7 +48,9 @@ The project currently includes fixed-case and randomised cocotb tests. The cocot | `test_random_reference.py` | Randomised input cases checked against Python reference model | | `reference_model.py` | Python software model of the expected RTL behaviour | -Example Output: +Vivado synthesis and timing reports are included in [`docs/synthesis.md`](docs/synthesis.md). + +Example `cocotb` Output: ```text 1000000.00ns INFO test 1000000 tests ran successfully... @@ -62,5 +63,4 @@ Example Output: ## Status -Working simple RTL pipeline with Verilog and cocotb verification. -A Python reference model is used to check fixed and randomised test cases against expected behaviour. +Synchronous RTL pipeline is implemented and verified with fixed and randomised cocotb tests against a Python reference model. Vivado synthesis and post-synthesis timing checks have also been run for the current design. diff --git a/constraints/constraints.xdc b/constraints/constraints.xdc new file mode 100644 index 0000000..4702f4c --- /dev/null +++ b/constraints/constraints.xdc @@ -0,0 +1 @@ +create_clock -period 10.000 -name clk [get_ports clk] \ No newline at end of file diff --git a/docs/synthesis.md b/docs/synthesis.md new file mode 100644 index 0000000..a4f8820 --- /dev/null +++ b/docs/synthesis.md @@ -0,0 +1,18 @@ +# Synthesis + +Vivado synthesis was run on the RTL pipeline targeting the Artix-7 XC7A35T device which is used on the Basys 3 board. + +## Summary + +| Item | Result | +| ----------------- | -------- | +| LUTs | 25 | +| Flip-Flops | 40 | +| Latches | 0 | +| Worst Setup Slack | 6.909 ns | +| Worst Hold Slack | 0.179 ns | + +## Full Reports + +- [`utilization_report.txt`](./utilization_report.txt) +- [`timing_summary.txt`](./timing_summary.txt) diff --git a/docs/timing_summary.txt b/docs/timing_summary.txt new file mode 100644 index 0000000..1698d9c --- /dev/null +++ b/docs/timing_summary.txt @@ -0,0 +1,332 @@ +Copyright 1986-2022 Xilinx, Inc. All Rights Reserved. Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. +--------------------------------------------------------------------------------------------------------------------------------------------- +| Tool Version : Vivado v.2025.2 (win64) Build 6299465 Fri Nov 14 19:35:11 GMT 2025 +| Date : Thu May 28 21:11:00 2026 +| Host : Zenbook running 64-bit major release (build 9200) +| Command : report_timing_summary -file timing_summary.txt +| Design : top_pipeline +| Device : 7a35t-cpg236 +| Speed File : -1 PRODUCTION 1.23 2018-06-13 +| Design State : Synthesized +--------------------------------------------------------------------------------------------------------------------------------------------- + +Timing Summary Report + +------------------------------------------------------------------------------------------------ +| Timer Settings +| -------------- +------------------------------------------------------------------------------------------------ + + Enable Multi Corner Analysis : Yes + Enable Pessimism Removal : Yes + Pessimism Removal Resolution : Nearest Common Node + Enable Input Delay Default Clock : No + Enable Preset / Clear Arcs : No + Disable Flight Delays : No + Ignore I/O Paths : No + Timing Early Launch at Borrowing Latches : No + Borrow Time for Max Delay Exceptions : Yes + Merge Timing Exceptions : Yes + Inter-SLR Compensation : Conservative + + Corner Analyze Analyze + Name Max Paths Min Paths + ------ --------- --------- + Slow Yes Yes + Fast Yes Yes + + +------------------------------------------------------------------------------------------------ +| Report Methodology +| ------------------ +------------------------------------------------------------------------------------------------ + +No report available as report_methodology has not been run prior. Run report_methodology on the current design for the summary of methodology violations. + + + +check_timing report + +Table of Contents +----------------- +1. checking no_clock (0) +2. checking constant_clock (0) +3. checking pulse_width_clock (0) +4. checking unconstrained_internal_endpoints (0) +5. checking no_input_delay (22) +6. checking no_output_delay (19) +7. checking multiple_clock (0) +8. checking generated_clocks (0) +9. checking loops (0) +10. checking partial_input_delay (0) +11. checking partial_output_delay (0) +12. checking latch_loops (0) + +1. checking no_clock (0) +------------------------ + There are 0 register/latch pins with no clock. + + +2. checking constant_clock (0) +------------------------------ + There are 0 register/latch pins with constant_clock. + + +3. checking pulse_width_clock (0) +--------------------------------- + There are 0 register/latch pins which need pulse_width check + + +4. checking unconstrained_internal_endpoints (0) +------------------------------------------------ + There are 0 pins that are not constrained for maximum delay. + + There are 0 pins that are not constrained for maximum delay due to constant clock. + + +5. checking no_input_delay (22) +------------------------------- + There are 22 input ports with no input delay specified. (HIGH) + + There are 0 input ports with no input delay but user has a false path constraint. + + +6. checking no_output_delay (19) +-------------------------------- + There are 19 ports with no output delay specified. (HIGH) + + There are 0 ports with no output delay but user has a false path constraint + + There are 0 ports with no output delay but with a timing clock defined on it or propagating through it + + +7. checking multiple_clock (0) +------------------------------ + There are 0 register/latch pins with multiple clocks. + + +8. checking generated_clocks (0) +-------------------------------- + There are 0 generated clocks that are not connected to a clock source. + + +9. checking loops (0) +--------------------- + There are 0 combinational loops in the design. + + +10. checking partial_input_delay (0) +------------------------------------ + There are 0 input ports with partial input delay specified. + + +11. checking partial_output_delay (0) +------------------------------------- + There are 0 ports with partial output delay specified. + + +12. checking latch_loops (0) +---------------------------- + There are 0 combinational latch loops in the design through latch input + + + +------------------------------------------------------------------------------------------------ +| Design Timing Summary +| --------------------- +------------------------------------------------------------------------------------------------ + + WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) TPWS(ns) TPWS Failing Endpoints TPWS Total Endpoints + ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- -------- ---------------------- -------------------- + 6.909 0.000 0 19 0.179 0.000 0 19 4.500 0.000 0 41 + + +All user specified timing constraints are met. + + +------------------------------------------------------------------------------------------------ +| Clock Summary +| ------------- +------------------------------------------------------------------------------------------------ + +Clock Waveform(ns) Period(ns) Frequency(MHz) +----- ------------ ---------- -------------- +clk {0.000 5.000} 10.000 100.000 + + +------------------------------------------------------------------------------------------------ +| Intra Clock Table +| ----------------- +------------------------------------------------------------------------------------------------ + +Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) TPWS(ns) TPWS Failing Endpoints TPWS Total Endpoints +----- ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- -------- ---------------------- -------------------- +clk 6.909 0.000 0 19 0.179 0.000 0 19 4.500 0.000 0 41 + + +------------------------------------------------------------------------------------------------ +| Inter Clock Table +| ----------------- +------------------------------------------------------------------------------------------------ + +From Clock To Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints +---------- -------- ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- + + +------------------------------------------------------------------------------------------------ +| Other Path Groups Table +| ----------------------- +------------------------------------------------------------------------------------------------ + +Path Group From Clock To Clock WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints +---------- ---------- -------- ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- + + +------------------------------------------------------------------------------------------------ +| Timing Details +| -------------- +------------------------------------------------------------------------------------------------ + + +--------------------------------------------------------------------------------------------------- +From Clock: clk + To Clock: clk + +Setup : 0 Failing Endpoints, Worst Slack 6.909ns, Total Violation 0.000ns +Hold : 0 Failing Endpoints, Worst Slack 0.179ns, Total Violation 0.000ns +PW : 0 Failing Endpoints, Worst Slack 4.500ns, Total Violation 0.000ns +--------------------------------------------------------------------------------------------------- + + +Max Delay Paths +-------------------------------------------------------------------------------------- +Slack (MET) : 6.909ns (required time - arrival time) + Source: reg_message_reg[12]/C + (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@5.000ns period=10.000ns}) + Destination: reject_reason_reg[1]/D + (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@5.000ns period=10.000ns}) + Path Group: clk + Path Type: Setup (Max at Slow Process Corner) + Requirement: 10.000ns (clk rise@10.000ns - clk rise@0.000ns) + Data Path Delay: 2.940ns (logic 0.999ns (33.980%) route 1.941ns (66.020%)) + Logic Levels: 3 (LUT6=3) + Clock Path Skew: -0.145ns (DCD - SCD + CPR) + Destination Clock Delay (DCD): 2.079ns = ( 12.079 - 10.000 ) + Source Clock Delay (SCD): 2.402ns + Clock Pessimism Removal (CPR): 0.178ns + Clock Uncertainty: 0.035ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE + Total System Jitter (TSJ): 0.071ns + Total Input Jitter (TIJ): 0.000ns + Discrete Jitter (DJ): 0.000ns + Phase Error (PE): 0.000ns + + Location Delay type Incr(ns) Path(ns) Netlist Resource(s) + ------------------------------------------------------------------- ------------------- + (clock clk rise edge) 0.000 0.000 r + 0.000 0.000 r clk (IN) + net (fo=0) 0.000 0.000 clk + IBUF (Prop_ibuf_I_O) 0.923 0.923 r clk_IBUF_inst/O + net (fo=1, unplaced) 0.800 1.722 clk_IBUF + BUFG (Prop_bufg_I_O) 0.096 1.818 r clk_IBUF_BUFG_inst/O + net (fo=40, unplaced) 0.584 2.402 clk_IBUF_BUFG + FDRE r reg_message_reg[12]/C + ------------------------------------------------------------------- ------------------- + FDRE (Prop_fdre_C_Q) 0.456 2.858 r reg_message_reg[12]/Q + net (fo=2, unplaced) 0.976 3.834 input_val_b[4] + LUT6 (Prop_lut6_I0_O) 0.295 4.129 r reject_reason[0]_i_2/O + net (fo=2, unplaced) 0.460 4.589 reject_reason[0]_i_2_n_0 + LUT6 (Prop_lut6_I4_O) 0.124 4.713 f value_a[7]_i_2/O + net (fo=17, unplaced) 0.505 5.218 value_a[7]_i_2_n_0 + LUT6 (Prop_lut6_I0_O) 0.124 5.342 r reject_reason[1]_i_1/O + net (fo=1, unplaced) 0.000 5.342 reject_reason[1]_i_1_n_0 + FDRE r reject_reason_reg[1]/D + ------------------------------------------------------------------- ------------------- + + (clock clk rise edge) 10.000 10.000 r + 0.000 10.000 r clk (IN) + net (fo=0) 0.000 10.000 clk + IBUF (Prop_ibuf_I_O) 0.789 10.789 r clk_IBUF_inst/O + net (fo=1, unplaced) 0.760 11.549 clk_IBUF + BUFG (Prop_bufg_I_O) 0.091 11.640 r clk_IBUF_BUFG_inst/O + net (fo=40, unplaced) 0.439 12.079 clk_IBUF_BUFG + FDRE r reject_reason_reg[1]/C + clock pessimism 0.178 12.257 + clock uncertainty -0.035 12.222 + FDRE (Setup_fdre_C_D) 0.029 12.251 reject_reason_reg[1] + ------------------------------------------------------------------- + required time 12.251 + arrival time -5.342 + ------------------------------------------------------------------- + slack 6.909 + + + + + +Min Delay Paths +-------------------------------------------------------------------------------------- +Slack (MET) : 0.179ns (arrival time - required time) + Source: reg_message_valid_reg/C + (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@5.000ns period=10.000ns}) + Destination: output_valid_reg/D + (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@5.000ns period=10.000ns}) + Path Group: clk + Path Type: Hold (Min at Fast Process Corner) + Requirement: 0.000ns (clk rise@0.000ns - clk rise@0.000ns) + Data Path Delay: 0.307ns (logic 0.141ns (45.980%) route 0.166ns (54.020%)) + Logic Levels: 0 + Clock Path Skew: 0.145ns (DCD - SCD - CPR) + Destination Clock Delay (DCD): 0.983ns + Source Clock Delay (SCD): 0.629ns + Clock Pessimism Removal (CPR): 0.209ns + + Location Delay type Incr(ns) Path(ns) Netlist Resource(s) + ------------------------------------------------------------------- ------------------- + (clock clk rise edge) 0.000 0.000 r + 0.000 0.000 r clk (IN) + net (fo=0) 0.000 0.000 clk + IBUF (Prop_ibuf_I_O) 0.152 0.152 r clk_IBUF_inst/O + net (fo=1, unplaced) 0.337 0.489 clk_IBUF + BUFG (Prop_bufg_I_O) 0.026 0.515 r clk_IBUF_BUFG_inst/O + net (fo=40, unplaced) 0.114 0.629 clk_IBUF_BUFG + FDRE r reg_message_valid_reg/C + ------------------------------------------------------------------- ------------------- + FDRE (Prop_fdre_C_Q) 0.141 0.770 r reg_message_valid_reg/Q + net (fo=19, unplaced) 0.166 0.936 reg_message_valid + FDRE r output_valid_reg/D + ------------------------------------------------------------------- ------------------- + + (clock clk rise edge) 0.000 0.000 r + 0.000 0.000 r clk (IN) + net (fo=0) 0.000 0.000 clk + IBUF (Prop_ibuf_I_O) 0.340 0.340 r clk_IBUF_inst/O + net (fo=1, unplaced) 0.355 0.695 clk_IBUF + BUFG (Prop_bufg_I_O) 0.029 0.724 r clk_IBUF_BUFG_inst/O + net (fo=40, unplaced) 0.259 0.983 clk_IBUF_BUFG + FDRE r output_valid_reg/C + clock pessimism -0.209 0.774 + FDRE (Hold_fdre_C_D) -0.017 0.757 output_valid_reg + ------------------------------------------------------------------- + required time -0.757 + arrival time 0.936 + ------------------------------------------------------------------- + slack 0.179 + + + + + +Pulse Width Checks +-------------------------------------------------------------------------------------- +Clock Name: clk +Waveform(ns): { 0.000 5.000 } +Period(ns): 10.000 +Sources: { clk } + +Check Type Corner Lib Pin Reference Pin Required(ns) Actual(ns) Slack(ns) Location Pin +Min Period n/a BUFG/I n/a 2.155 10.000 7.845 clk_IBUF_BUFG_inst/I +Low Pulse Width Slow FDRE/C n/a 0.500 5.000 4.500 output_valid_reg/C +High Pulse Width Slow FDRE/C n/a 0.500 5.000 4.500 output_valid_reg/C + + + diff --git a/docs/utilization_report.txt b/docs/utilization_report.txt new file mode 100644 index 0000000..1a138b7 --- /dev/null +++ b/docs/utilization_report.txt @@ -0,0 +1,183 @@ +Copyright 1986-2022 Xilinx, Inc. All Rights Reserved. Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +| Tool Version : Vivado v.2025.2 (win64) Build 6299465 Fri Nov 14 19:35:11 GMT 2025 +| Date : Thu May 28 20:45:19 2026 +| Host : Zenbook running 64-bit major release (build 9200) +| Command : report_utilization -hierarchical_min_primitive_count 50 -file C:/Users/harry/code/personal/fpga-stream-processor/fpga-stream-processor/utilization_report.txt -name utilization_2 +| Design : top_pipeline +| Device : xc7a35tcpg236-1 +| Speed File : -1 +| Design State : Synthesized +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Utilization Design Information + +Table of Contents +----------------- +1. Slice Logic +1.1 Summary of Registers by Type +2. Memory +3. DSP +4. IO and GT Specific +5. Clocking +6. Specific Feature +7. Primitives +8. Black Boxes +9. Instantiated Netlists + +1. Slice Logic +-------------- + ++-------------------------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++-------------------------+------+-------+------------+-----------+-------+ +| Slice LUTs* | 25 | 0 | 0 | 20800 | 0.12 | +| LUT as Logic | 25 | 0 | 0 | 20800 | 0.12 | +| LUT as Memory | 0 | 0 | 0 | 9600 | 0.00 | +| Slice Registers | 40 | 0 | 0 | 41600 | 0.10 | +| Register as Flip Flop | 40 | 0 | 0 | 41600 | 0.10 | +| Register as Latch | 0 | 0 | 0 | 41600 | 0.00 | +| F7 Muxes | 0 | 0 | 0 | 16300 | 0.00 | +| F8 Muxes | 0 | 0 | 0 | 8150 | 0.00 | +| Unique Control Sets | 1 | | 0 | 8150 | 0.01 | ++-------------------------+------+-------+------------+-----------+-------+ +* Warning! The Final LUT count, after physical optimizations and full implementation, is typically lower. Run opt_design after synthesis, if not already completed, for a more realistic count. +Warning! LUT value is adjusted to account for LUT combining. +Warning! For any ECO changes, please run place_design if there are unplaced instances +** Note: Available Control Sets calculated as Slice * 1, Review the Control Sets Report for more information regarding control sets. + + +1.1 Summary of Registers by Type +-------------------------------- + ++-------+--------------+-------------+--------------+ +| Total | Clock Enable | Synchronous | Asynchronous | ++-------+--------------+-------------+--------------+ +| 0 | _ | - | - | +| 0 | _ | - | Set | +| 0 | _ | - | Reset | +| 0 | _ | Set | - | +| 0 | _ | Reset | - | +| 0 | Yes | - | - | +| 0 | Yes | - | Set | +| 0 | Yes | - | Reset | +| 0 | Yes | Set | - | +| 40 | Yes | Reset | - | ++-------+--------------+-------------+--------------+ + + +2. Memory +--------- + ++----------------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++----------------+------+-------+------------+-----------+-------+ +| Block RAM Tile | 0 | 0 | 0 | 50 | 0.00 | +| RAMB36/FIFO* | 0 | 0 | 0 | 50 | 0.00 | +| RAMB18 | 0 | 0 | 0 | 100 | 0.00 | ++----------------+------+-------+------------+-----------+-------+ +* Note: Each Block RAM Tile only has one FIFO logic available and therefore can accommodate only one FIFO36E1 or one FIFO18E1. However, if a FIFO18E1 occupies a Block RAM Tile, that tile can still accommodate a RAMB18E1 + + +3. DSP +------ + ++-----------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++-----------+------+-------+------------+-----------+-------+ +| DSPs | 0 | 0 | 0 | 90 | 0.00 | ++-----------+------+-------+------------+-----------+-------+ + + +4. IO and GT Specific +--------------------- + ++-----------------------------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++-----------------------------+------+-------+------------+-----------+-------+ +| Bonded IOB | 44 | 0 | 0 | 106 | 41.51 | +| Bonded IPADs | 0 | 0 | 0 | 10 | 0.00 | +| Bonded OPADs | 0 | 0 | 0 | 4 | 0.00 | +| PHY_CONTROL | 0 | 0 | 0 | 5 | 0.00 | +| PHASER_REF | 0 | 0 | 0 | 5 | 0.00 | +| OUT_FIFO | 0 | 0 | 0 | 20 | 0.00 | +| IN_FIFO | 0 | 0 | 0 | 20 | 0.00 | +| IDELAYCTRL | 0 | 0 | 0 | 5 | 0.00 | +| IBUFDS | 0 | 0 | 0 | 104 | 0.00 | +| GTPE2_CHANNEL | 0 | 0 | 0 | 2 | 0.00 | +| PHASER_OUT/PHASER_OUT_PHY | 0 | 0 | 0 | 20 | 0.00 | +| PHASER_IN/PHASER_IN_PHY | 0 | 0 | 0 | 20 | 0.00 | +| IDELAYE2/IDELAYE2_FINEDELAY | 0 | 0 | 0 | 250 | 0.00 | +| IBUFDS_GTE2 | 0 | 0 | 0 | 2 | 0.00 | +| ILOGIC | 0 | 0 | 0 | 106 | 0.00 | +| OLOGIC | 0 | 0 | 0 | 106 | 0.00 | ++-----------------------------+------+-------+------------+-----------+-------+ + + +5. Clocking +----------- + ++------------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++------------+------+-------+------------+-----------+-------+ +| BUFGCTRL | 1 | 0 | 0 | 32 | 3.13 | +| BUFIO | 0 | 0 | 0 | 20 | 0.00 | +| MMCME2_ADV | 0 | 0 | 0 | 5 | 0.00 | +| PLLE2_ADV | 0 | 0 | 0 | 5 | 0.00 | +| BUFMRCE | 0 | 0 | 0 | 10 | 0.00 | +| BUFHCE | 0 | 0 | 0 | 72 | 0.00 | +| BUFR | 0 | 0 | 0 | 20 | 0.00 | ++------------+------+-------+------------+-----------+-------+ + + +6. Specific Feature +------------------- + ++-------------+------+-------+------------+-----------+-------+ +| Site Type | Used | Fixed | Prohibited | Available | Util% | ++-------------+------+-------+------------+-----------+-------+ +| BSCANE2 | 0 | 0 | 0 | 4 | 0.00 | +| CAPTUREE2 | 0 | 0 | 0 | 1 | 0.00 | +| DNA_PORT | 0 | 0 | 0 | 1 | 0.00 | +| EFUSE_USR | 0 | 0 | 0 | 1 | 0.00 | +| FRAME_ECCE2 | 0 | 0 | 0 | 1 | 0.00 | +| ICAPE2 | 0 | 0 | 0 | 2 | 0.00 | +| PCIE_2_1 | 0 | 0 | 0 | 1 | 0.00 | +| STARTUPE2 | 0 | 0 | 0 | 1 | 0.00 | +| XADC | 0 | 0 | 0 | 1 | 0.00 | ++-------------+------+-------+------------+-----------+-------+ + + +7. Primitives +------------- + ++----------+------+---------------------+ +| Ref Name | Used | Functional Category | ++----------+------+---------------------+ +| FDRE | 40 | Flop & Latch | +| IBUF | 23 | IO | +| OBUF | 21 | IO | +| LUT2 | 20 | LUT | +| LUT4 | 17 | LUT | +| LUT6 | 5 | LUT | +| LUT3 | 1 | LUT | +| BUFG | 1 | Clock | ++----------+------+---------------------+ + + +8. Black Boxes +-------------- + ++----------+------+ +| Ref Name | Used | ++----------+------+ + + +9. Instantiated Netlists +------------------------ + ++----------+------+ +| Ref Name | Used | ++----------+------+ + + diff --git a/docs/waveforms.md b/docs/waveforms.md new file mode 100644 index 0000000..fe2ab1c --- /dev/null +++ b/docs/waveforms.md @@ -0,0 +1,9 @@ +# Waveforms + +This doc contains waveforms from RTL pipeline testing. + +## Synchronous Pipeline Test + +The waveform shows the synchronous version of the pipeline being tested with fixed input messages. + +![Synchronous pipeline waveform](../images/sync_tb_waveform.png) diff --git a/images/sync_tb_waveform.png b/images/sync_tb_waveform.png new file mode 100644 index 0000000..474890a Binary files /dev/null and b/images/sync_tb_waveform.png differ diff --git a/rtl/README.md b/rtl/README.md index 1ddcb32..aa28417 100644 --- a/rtl/README.md +++ b/rtl/README.md @@ -4,25 +4,35 @@ This folder contains the Verilog design files for the project. ## Current status -The current setup includes a simple message parser, a filter to check for an invalid type and a pipeline to connect these and decide the output based on the parsed input and filter result. - -```text - ┌────────────────┐ message type ┌────────────────┐ - message │ ├──────────────────►│ │ reject reason -───────────────►│ message_parser │ │ message_filter ├───────────► - │ ├──────────────────►│ │ - └────────────────┘ values a&b └────┬────┬──────┘ - │ │ - │ │ - │ │ - │ │ - │ │ - input a&b│ │ valid message? - │ │ ┌───────────────────┐ output a&b - │ └───►│ │ - │ │ output_formatter ├───────────► - └────────►│ │ - └───────────────────┘ +The RTL implements a synchronous message-processing pipeline. + +`top_pipeline.v` provides a clocked wrapper, including reset handling, input sampling, registered outputs, and `input_valid` / `output_valid` signalling. + +The internal processing modules are all combinational: + +- `message_parser.v` splits the 32-bit input message into seperate fields. +- `message_filter.v` checks the parsed fields and produces an accept/reject decision. +- `output_formatter.v` sets the registered output values based on the filter result. + +```mermaid +flowchart LR + A("input") --> |"32-bit message"| B["input register"] + A --> |"input_valid"| B + + CLK["clk & reset"] -.-> B + CLK -.-> F["output registers"] + + B --> C["message_parser"] + C --> |message_type| D["message_filter"] + C --> |value_a & value_b| D + C --> |value_a & value_b| E["output_formatter"] + D --> |accept/reject| E + D --> |reject_reason| F + E --> |formatted values| F + + F --> |"value_a & value_b"| G("output") + F --> |"reject_reason"| G + F --> |"output_valid"| G ``` ## Message format diff --git a/rtl/message_filter.v b/rtl/message_filter.v index 9027285..e137499 100644 --- a/rtl/message_filter.v +++ b/rtl/message_filter.v @@ -27,7 +27,7 @@ module message_filter else begin message_valid = 1; reject_reason = 4'b0000; // passes through filter - end + end end endmodule diff --git a/rtl/top_pipeline.v b/rtl/top_pipeline.v index 75f2c17..f2912cf 100644 --- a/rtl/top_pipeline.v +++ b/rtl/top_pipeline.v @@ -1,28 +1,81 @@ `timescale 1ns / 1ps module top_pipeline( + input clk, + input reset, + input input_valid, // should input be ignored input wire [31:0] message, - output wire [7:0] value_a, - output wire [7:0] value_b, - output wire [3:0] reject_reason + + output reg output_valid, // should output be ignored + output reg [7:0] value_a, + output reg [7:0] value_b, + output reg [3:0] reject_reason ); + reg [31:0] reg_message; + reg reg_message_valid; + + wire [7:0] value_a_wire; + wire [7:0] value_b_wire; + + wire message_accept; + wire [3:0] reject_reason_wire; + + always @ (posedge clk) begin // synchronous logic + // input sampling logic + if (reset) begin // reset HIGH + reg_message <= 0; + reg_message_valid <= 0; // current reg message is invalid + end + else if (input_valid) begin + reg_message <= message; // sample input on clk edge if its valid + reg_message_valid <= 1; // reg message is valid + end + else begin // if input invalid, ignore the input + reg_message <= 0; + reg_message_valid <= 0; + end + + // output sampling logic + output_valid <= reg_message_valid; // output valid based on prev message valid + + if (reset) begin // reset HIGH + output_valid <= 0; // output invalid during reset + value_a <= 0; + value_b <= 0; + reject_reason <= 0; + end + else if (reg_message_valid) begin // if current reg message is valid, sample output + value_a <= value_a_wire; //outputs will be based on prev cycle input + value_b <= value_b_wire; + reject_reason <= reject_reason_wire; + end + else begin //outputs zeroed if reg input is invalid + value_a <= 0; + value_b <= 0; + reject_reason <= 0; + end + end + // parse diferent vars from the input message wire [7:0] input_val_a, input_val_b; wire [3:0] message_type; + wire [3:0] channel_id; // will be used in fut ver + wire [7:0] flags; // will be used in fut ver message_parser parser ( - .message(message), + .message(reg_message), // input the most recent sampled input .value_a(input_val_a), .value_b(input_val_b), - .message_type(message_type) + .message_type(message_type), + .channel_id(channel_id), + .flags(flags) ); // pass parsed values into message filter - wire message_valid; message_filter filter ( .message_type(message_type), - .message_valid(message_valid), - .reject_reason(reject_reason), + .message_valid(message_accept), + .reject_reason(reject_reason_wire), .value_a(input_val_a), .value_b(input_val_b) ); @@ -31,9 +84,9 @@ module top_pipeline( output_formatter out_format ( .input_val_a(input_val_a), .input_val_b(input_val_b), - .message_valid(message_valid), - .value_a(value_a), - .value_b(value_b) + .message_valid(message_accept), + .value_a(value_a_wire), + .value_b(value_b_wire) ); endmodule diff --git a/tb/cocotb/README.md b/tb/cocotb/README.md index ff173c1..ff63382 100644 --- a/tb/cocotb/README.md +++ b/tb/cocotb/README.md @@ -31,11 +31,12 @@ sudo apt install iverilog ## Current status Current verification includes fixed cocotb tests, a Python reference model, and randomised RTL vs model testing. +The cocotb tests drive the synchronous pipeline by generating `clk`, `reset`, `input_valid`, and `output_valid`. - [x] Recreate Verilog TB in Python - [x] Write a Python software reference model to compare to the hardware Verilog design - [x] Randomised input testing against reference model -- [ ] Edge case testing +- [X] Edge case testing ## Files diff --git a/tb/cocotb/test_direct_cases.py b/tb/cocotb/test_direct_cases.py index 356bb90..c619b38 100644 --- a/tb/cocotb/test_direct_cases.py +++ b/tb/cocotb/test_direct_cases.py @@ -1,18 +1,37 @@ # Fixed tests from Verilog Testbench, manually checked output import cocotb -from cocotb.triggers import Timer - -import reference_model +from cocotb.triggers import RisingEdge, ClockCycles, ReadOnly +from cocotb.clock import Clock # Design under test @cocotb.test() async def fixed_direct_cases(dut): + # set up a 10ns cycle clock + clk = Clock(dut.clk, 10, "ns") + clk.start() + + # set reset HIGH for start + dut.reset.value = 1 + dut.input_valid.value = 0 + dut.message.value = 0 + + await ClockCycles(dut.clk, 2) # wait two clock cycles, holding reset HIGH + + dut.reset.value = 0 # disable reset signal + # === Test Case 1 ==== # message = 0b0000_0000_11111111_00001111_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #255 value_b = int(dut.value_b.value) #15 @@ -25,8 +44,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 2 # === Test Case 2 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0000_0000_11110000_11110000_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #240 value_b = int(dut.value_b.value) #240 @@ -39,8 +66,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 2 # === Test Case 3 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0000_0000_01010101_11001101_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #85 value_b = int(dut.value_b.value) #205 @@ -53,8 +88,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 3 # === Test Case 4 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0000_0000_00000101_00000011_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #5 value_b = int(dut.value_b.value) #3 @@ -67,8 +110,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 0 # === Test Case 5 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0000_0000_11001000_11001000_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #200 value_b = int(dut.value_b.value) #200 @@ -81,8 +132,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 0 # === Test Case 6 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0001_0000_01010101_10101010_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #200 value_b = int(dut.value_b.value) #200 @@ -95,8 +154,16 @@ async def fixed_direct_cases(dut): assert reject_reason == 1 # === Test Case 7 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge dut.message.value = 0b0001_0000_11110000_00001111_00000000 - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) value_b = int(dut.value_b.value) diff --git a/tb/cocotb/test_random_reference.py b/tb/cocotb/test_random_reference.py index 45c17c1..e3fd143 100644 --- a/tb/cocotb/test_random_reference.py +++ b/tb/cocotb/test_random_reference.py @@ -1,23 +1,43 @@ # Raondmised tests with outputs compared against Python Reference Model import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import RisingEdge, ClockCycles, ReadOnly +from cocotb.clock import Clock from reference_model import reference_model import message_generator -NUM_TESTS = 1_000 # number of testcases to generate +NUM_TESTS = 10_000 # number of testcases to generate # Design under test @cocotb.test() async def random_reference_cases(dut): + # set up a 10ns cycle clock + clk = Clock(dut.clk, 10, "ns") + clk.start() + # set reset HIGH for start + dut.reset.value = 1 + dut.input_valid.value = 0 + dut.message.value = 0 + + await ClockCycles(dut.clk, 2) # wait two clock cycles, holding reset HIGH + + dut.reset.value = 0 # disable reset signal + + # begin test cases for i in range(NUM_TESTS): # generate random message, only allowing message type of 0 or 1 message = message_generator.generate_paramaterised_random_message(type_limit=1) - dut.message.value = message - await Timer(1, unit="ns") # allow message input value to settle before reading output + + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled allow message input value to settle before reading output + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage # record output values from sim value_a = int(dut.value_a.value) @@ -37,5 +57,8 @@ async def random_reference_cases(dut): assert reject_reason == ref_results["reject_reason"], \ f"FAIL: simulated reject_reason ({reject_reason}) =/= model reject_reason ({ref_results["reject_reason"]})" + await RisingEdge(dut.clk) # set next message at next rising edge + + cocotb.log.info(f"{NUM_TESTS} tests ran successfully...") cocotb.log.info(f"{i+1}/{NUM_TESTS}: PASS") \ No newline at end of file diff --git a/tb/cocotb/test_reference_cases.py b/tb/cocotb/test_reference_cases.py index 73a3519..e212e07 100644 --- a/tb/cocotb/test_reference_cases.py +++ b/tb/cocotb/test_reference_cases.py @@ -1,7 +1,8 @@ # Fixed tests from Verilog Testbench, output checked against Python reference model import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import RisingEdge, ClockCycles, ReadOnly +from cocotb.clock import Clock from reference_model import reference_model @@ -9,11 +10,30 @@ @cocotb.test() async def fixed_reference_cases(dut): + # set up a 10ns cycle clock + clk = Clock(dut.clk, 10, "ns") + clk.start() + + # set reset HIGH for start + dut.reset.value = 1 + dut.input_valid.value = 0 + dut.message.value = 0 + + await ClockCycles(dut.clk, 2) # wait two clock cycles, holding reset HIGH + + dut.reset.value = 0 # disable reset signal + # === Test Case 1 ==== # message = 0b0000_0000_11111111_00001111_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #255 value_b = int(dut.value_b.value) #15 @@ -29,10 +49,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 2 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0000_0000_11110000_11110000_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #240 value_b = int(dut.value_b.value) #240 @@ -48,10 +75,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 3 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0000_0000_01010101_11001101_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #85 value_b = int(dut.value_b.value) #205 @@ -67,10 +101,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 4 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0000_0000_00000101_00000011_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #5 value_b = int(dut.value_b.value) #3 @@ -86,10 +127,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 5 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0000_0000_11001000_11001000_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #200 value_b = int(dut.value_b.value) #200 @@ -105,10 +153,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 6 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0001_0000_01010101_10101010_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) #200 value_b = int(dut.value_b.value) #200 @@ -124,10 +179,17 @@ async def fixed_reference_cases(dut): assert reject_reason == ref_results["reject_reason"] # === Test Case 7 ==== # + await RisingEdge(dut.clk) # set next message at next rising edge message = 0b0001_0000_11110000_00001111_00000000 dut.message.value = message - await Timer(1, unit="ns") # allow message value to settle + dut.input_valid.value = 1 + await RisingEdge(dut.clk) # wait until posedge for input to be sampled + + dut.input_valid.value = 0 # don't read input after prev was sampled + + await RisingEdge(dut.clk) # wait for next posedge, output updated + await ReadOnly() # wait for values to settle and sim to enter "read only" stage value_a = int(dut.value_a.value) value_b = int(dut.value_b.value) diff --git a/tb/verilog/README.md b/tb/verilog/README.md index 9220a7e..c8dfc8e 100644 --- a/tb/verilog/README.md +++ b/tb/verilog/README.md @@ -4,5 +4,5 @@ This folder contains simple verilog simulation testbenches for the RTL modules. ## Current status -The current testbench inputs fixed 32-bit messages into `top_pipeline.v` and checks the parsed fields in simulation. +The top-level testbench applies fixed 32-bit messages to the synchronous pipeline and checks the registered output values and reject reasons. Outputs are compared against expected values and a count for the number of passes/fails is tracked. diff --git a/tb/verilog/message_parser_tb.v b/tb/verilog/message_parser_tb.v index e64a36c..e344f78 100644 --- a/tb/verilog/message_parser_tb.v +++ b/tb/verilog/message_parser_tb.v @@ -1,21 +1,138 @@ `timescale 1ns / 1ps -module message_parser_tb(); +module top_pipeline_tb(); - // generate some simple test messages, opnly changing values of a and b reg [31:0] message; + wire [7:0] value_a; + wire [7:0] value_b; + wire [3:0] reject_reason; + + reg [7:0] test_value_a; + reg [7:0] test_value_b; + reg [3:0] test_reject_reason; + wire output_valid; + + reg clk; + initial + clk = 0; + always + #5 clk = ~clk; //setup simple testbench clock + + reg reset, input_valid; + initial begin // set reset HIGH for one clock cycle on start + reset = 1; + #10 + + reset = 0; + end + + reg [7:0] fail_count; + reg [7:0] pass_count; initial begin - message = 32'b0000_0000_11111111_00001111_00000000; #10 - message = 32'b0000_0000_11110000_11110000_00000000; #10 - message = 32'b0000_0000_00000000_00000000_00000000; + fail_count = 0; + pass_count = 0; end - // read out the 2 values only for tb - wire [7:0] value_a; - wire [7:0] value_b; + always @ (posedge output_valid) begin // pass/fail checks sampled when output valid + if (!reset) begin + if (value_a == test_value_a && + value_b == test_value_b && + reject_reason == test_reject_reason) + // keep track of num failing and passing test cases + + pass_count <= pass_count + 1; + else + fail_count <= fail_count + 1; + end + end + + initial begin + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0000_0000_11111111_00001111_00000000; + + test_value_a = 8'b00000000; //255 + test_value_b = 8'b00000000; //15 + test_reject_reason = 4'b0010; + #10 // invalid - A too high + + input_valid = 0; #10 + input_valid = 1; + + + message = 32'b0000_0000_11110000_11110000_00000000; - // feed input and outputs relevant vars - message_parser uut (.value_a(value_a), .value_b(value_b), .message(message)); + test_value_a = 8'b00000000; //240 + test_value_b = 8'b00000000; //240 + test_reject_reason = 4'b0010; + #10 // invalid - A too high (checks A first) + + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0000_0000_01010101_11001101_00000000; + + test_value_a = 8'b00000000; //85 + test_value_b = 8'b00000000; //205 + test_reject_reason = 4'b0011; + #10 // invalid - B too high + + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0000_0000_00000101_00000011_00000000; + + test_value_a = 8'b00000101; //5 + test_value_b = 8'b00000011; //3 + test_reject_reason = 4'b0000; + #10 // valid - passes filter + + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0000_0000_11001000_11001000_00000000; + + test_value_a = 8'b11001000; //200 + test_value_b = 8'b11001000; //200 + test_reject_reason = 4'b0000; + #10 // valid - passes filter + + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0001_0000_01010101_10101010_00000000; + + test_value_a = 8'b00000000; + test_value_b = 8'b00000000; + test_reject_reason = 4'b0001; + #10 // invalid - wrong type + + input_valid = 0; #10 + input_valid = 1; + + message = 32'b0001_0000_11110000_00001111_00000000; + + test_value_a = 8'b00000000; + test_value_b = 8'b00000000; + test_reject_reason = 4'b0001; // invalid - wrong type + #10 + + input_valid = 0; #10 + + $display("Passes: %0d, Fails: %0d", pass_count, fail_count); // finish sim and display results + $finish; + end + top_pipeline uut ( + .clk(clk), + .reset(reset), + .input_valid(input_valid), + .output_valid(output_valid), + .message(message), + .value_a(value_a), + .value_b(value_b), + .reject_reason(reject_reason) + ); endmodule diff --git a/tb/verilog/top_pipeline_tb.v b/tb/verilog/top_pipeline_tb.v index 3beb379..e344f78 100644 --- a/tb/verilog/top_pipeline_tb.v +++ b/tb/verilog/top_pipeline_tb.v @@ -10,6 +10,7 @@ module top_pipeline_tb(); reg [7:0] test_value_a; reg [7:0] test_value_b; reg [3:0] test_reject_reason; + wire output_valid; reg clk; initial @@ -17,6 +18,14 @@ module top_pipeline_tb(); always #5 clk = ~clk; //setup simple testbench clock + reg reset, input_valid; + initial begin // set reset HIGH for one clock cycle on start + reset = 1; + #10 + + reset = 0; + end + reg [7:0] fail_count; reg [7:0] pass_count; initial begin @@ -24,65 +33,102 @@ module top_pipeline_tb(); pass_count = 0; end - always @ (posedge clk) begin - if (value_a == test_value_a && - value_b == test_value_b && - reject_reason == test_reject_reason) - // keep track of num failing and passing test cases - - pass_count <= pass_count + 1; - else - fail_count <= fail_count + 1; + always @ (posedge output_valid) begin // pass/fail checks sampled when output valid + if (!reset) begin + if (value_a == test_value_a && + value_b == test_value_b && + reject_reason == test_reject_reason) + // keep track of num failing and passing test cases + + pass_count <= pass_count + 1; + else + fail_count <= fail_count + 1; + end end initial begin + input_valid = 0; #10 + input_valid = 1; + message = 32'b0000_0000_11111111_00001111_00000000; + test_value_a = 8'b00000000; //255 test_value_b = 8'b00000000; //15 test_reject_reason = 4'b0010; - #10 // invalid - A too highj + #10 // invalid - A too high + + input_valid = 0; #10 + input_valid = 1; + message = 32'b0000_0000_11110000_11110000_00000000; + test_value_a = 8'b00000000; //240 test_value_b = 8'b00000000; //240 test_reject_reason = 4'b0010; #10 // invalid - A too high (checks A first) + input_valid = 0; #10 + input_valid = 1; + message = 32'b0000_0000_01010101_11001101_00000000; + test_value_a = 8'b00000000; //85 test_value_b = 8'b00000000; //205 test_reject_reason = 4'b0011; #10 // invalid - B too high + input_valid = 0; #10 + input_valid = 1; + message = 32'b0000_0000_00000101_00000011_00000000; + test_value_a = 8'b00000101; //5 test_value_b = 8'b00000011; //3 test_reject_reason = 4'b0000; #10 // valid - passes filter + input_valid = 0; #10 + input_valid = 1; + message = 32'b0000_0000_11001000_11001000_00000000; + test_value_a = 8'b11001000; //200 test_value_b = 8'b11001000; //200 test_reject_reason = 4'b0000; #10 // valid - passes filter + input_valid = 0; #10 + input_valid = 1; + message = 32'b0001_0000_01010101_10101010_00000000; + test_value_a = 8'b00000000; test_value_b = 8'b00000000; test_reject_reason = 4'b0001; #10 // invalid - wrong type + input_valid = 0; #10 + input_valid = 1; + message = 32'b0001_0000_11110000_00001111_00000000; + test_value_a = 8'b00000000; test_value_b = 8'b00000000; test_reject_reason = 4'b0001; // invalid - wrong type #10 + input_valid = 0; #10 + $display("Passes: %0d, Fails: %0d", pass_count, fail_count); // finish sim and display results $finish; end top_pipeline uut ( + .clk(clk), + .reset(reset), + .input_valid(input_valid), + .output_valid(output_valid), .message(message), .value_a(value_a), .value_b(value_b),