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.
+
+
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),