From 6ba3d27334d042e5865662fe5cb8375945e13f96 Mon Sep 17 00:00:00 2001 From: Zexin Fu Date: Sun, 26 Nov 2023 14:59:34 +0100 Subject: [PATCH] initial commit for open source NoC IP --- doc/image/router_microarchitecture.drawio.png | Bin 0 -> 102035 bytes doc/image/rrv64_noc_intf_to_soc.svg | 1 + doc/image/rrv64_noc_router_intf.svg | 1 + doc/noc_intf.md | 59 + doc/noc_spec.md | 538 ++++++ doc/router_spec.md | 99 + doc/system_memory_map.md | 50 + env/sourceme | 7 + flow/lint/Makefile | 14 + flow/lint/rvh_core.prj | 25 + flow/lint/spyglass-run-design_read.tcl | 21 + flow/lint/spyglass-run-lint_lint_rtl.tcl | 21 + flow/syn/Makefile | 33 + flow/syn/tcl_scripts/assert.tcl | 53 + flow/syn/tcl_scripts/constraints.sdc | 315 ++++ flow/syn/tcl_scripts/file_to_list.tcl | 139 ++ flow/syn/tcl_scripts/parse_arg.tcl | 130 ++ flow/syn/tcl_scripts/synth.tcl | 257 +++ flow/syn/tcl_scripts/synth_init_lib.tcl | 4 + .../tcl_scripts/synth_init_library.t12.tcl | 57 + rtl/include/rvh_noc_pkg.sv | 157 ++ rtl/input_port.sv | 138 ++ rtl/input_port_flit_decoder.md | 8 + rtl/input_port_flit_decoder.sv | 24 + rtl/input_port_vc.sv | 289 +++ rtl/input_to_output.sv | 516 ++++++ rtl/local_port_couple_module.sv | 329 ++++ rtl/local_port_look_adead_routing.sv | 93 + rtl/look_adead_routing.sv | 96 + rtl/model/cells/rrv64_cell_clkgate.v | 30 + rtl/model/cells/std_dff.sv | 27 + rtl/model/cells/std_dffe.sv | 31 + rtl/model/cells/std_dffr.sv | 34 + rtl/model/cells/std_dffre.sv | 36 + rtl/model/cells/std_dffrve.sv | 38 + rtl/model/simple_dual_one_clock.v | 47 + rtl/output_port_vc_assignment.sv | 268 +++ rtl/output_port_vc_credit_counter.sv | 97 + rtl/output_port_vc_selection.sv | 108 ++ rtl/performance_monitor.sv | 175 ++ rtl/priority_req_select.sv | 43 + rtl/ruby/ut_lib.sv | 467 +++++ rtl/sa_global.sv | 101 ++ rtl/sa_local.sv | 142 ++ rtl/switch.sv | 279 +++ rtl/util/age_order_selector.sv | 143 ++ rtl/util/age_order_selector_with_head.sv | 203 +++ rtl/util/commoncell/.gitmodules | 3 + rtl/util/commoncell/CommonCell.yaml | 5 + rtl/util/commoncell/README.md | 92 + rtl/util/commoncell/doc/.gitignore | 0 rtl/util/commoncell/env/sourceme | 11 + rtl/util/commoncell/src/Basic/Basic.yaml | 67 + .../commoncell/src/Basic/dv/CountOne_tb.v | 59 + rtl/util/commoncell/src/Basic/dv/MuxOH_tb.v | 66 + rtl/util/commoncell/src/Basic/dv/PLRU_tb.v | 49 + .../commoncell/src/Basic/dv/StreamFIFO_tb.v | 83 + .../commoncell/src/Basic/dv/SyncFIFO_tb.v | 83 + rtl/util/commoncell/src/Basic/hw/CountOne.v | 42 + rtl/util/commoncell/src/Basic/hw/MuxOH.v | 21 + rtl/util/commoncell/src/Basic/hw/OH2UInt.v | 25 + rtl/util/commoncell/src/Basic/hw/PLRU.v | 48 + rtl/util/commoncell/src/Basic/hw/PLRUTree.v | 64 + .../commoncell/src/Basic/hw/PriorityMux.v | 24 + rtl/util/commoncell/src/Basic/hw/StreamFIFO.v | 52 + rtl/util/commoncell/src/Basic/hw/SyncFIFO.v | 129 ++ rtl/util/commoncell/src/Queue/Queue.yaml | 58 + .../src/Queue/dv/AgeMatrixSelector_tb.v | 168 ++ .../Queue/dv/FIFO/MultiPortStreamFIFO_tb.v | 111 ++ .../src/Queue/dv/StaticPrioritySelector_tb.v | 99 + .../src/Queue/hw/AgeMatrixSelector.v | 187 ++ .../src/Queue/hw/FIAO/FIAOWithAgeMatrix.v | 61 + .../src/Queue/hw/FIAO/FIAOWithQueueManager.v | 126 ++ .../src/Queue/hw/FIFO/MultiPortStreamFIFO.v | 124 ++ .../commoncell/src/Queue/hw/QueueManager.v | 206 +++ .../src/Queue/hw/StaticPrioritySelector.v | 52 + rtl/util/commoncell/src/StdDFF/StdDFF.yaml | 39 + .../src/StdDFF/dv/DFFUncertainChecker.v | 17 + rtl/util/commoncell/src/StdDFF/hw/DFF.v | 25 + rtl/util/commoncell/src/StdDFF/hw/DFFE.v | 28 + rtl/util/commoncell/src/StdDFF/hw/DFFR.v | 31 + rtl/util/commoncell/src/StdDFF/hw/DFFRE.v | 35 + rtl/util/commoncell/tools/pico/.gitignore | 1 + .../commoncell/tools/pico/PackageParser.py | 132 ++ rtl/util/commoncell/tools/pico/README.md | 2 + .../tools/pico/defaultToolOption.yaml | 8 + rtl/util/commoncell/tools/pico/pico | 127 ++ rtl/util/freelist.sv | 148 ++ rtl/util/inorder_router.sv | 67 + rtl/util/left_circular_rotate.sv | 15 + rtl/util/mp_fifo.sv | 91 + rtl/util/mp_fifo_ptr_output.sv | 94 + rtl/util/mp_freelist.sv | 134 ++ rtl/util/oh2idx.sv | 29 + rtl/util/one_counter.sv | 50 + rtl/util/one_hot_priority_encoder.sv | 28 + rtl/util/one_hot_rr_arb.sv | 128 ++ rtl/util/onehot_mux.sv | 37 + rtl/util/plru.sv | 121 ++ rtl/util/priority_encoder.sv | 43 + rtl/util/pseudo_dual_ram.sv | 82 + rtl/util/pseudo_ram.sv | 61 + rtl/util/reg_dual_ram.sv | 45 + rtl/util/select_two_from_n_valid.sv | 62 + rtl/util/sp_fifo_dat_vld_output.sv | 106 ++ rtl/util/usage_manager.sv | 274 +++ rtl/vnet_router.sv | 1593 +++++++++++++++++ tb/Makefile | 40 + tb/flist_mesh.f | 59 + tb/flist_mesh.syn.f | 47 + tb/flist_single_router.f | 59 + tb/flist_single_router.syn.f | 47 + tb/tb_mesh.sv | 567 ++++++ tb/tb_single_router.sv | 559 ++++++ tb/testbench.sv | 174 ++ tb/top_mesh_syn.sv | 307 ++++ tb/top_single_router_syn.sv | 121 ++ tb/v_noc_pkg.sv | 53 + tb/v_receiver.sv | 56 + tb/v_scoreboard.sv | 467 +++++ tb/v_sender.sv | 198 ++ tb/v_test_generator.sv | 642 +++++++ 122 files changed, 14907 insertions(+) create mode 100644 doc/image/router_microarchitecture.drawio.png create mode 100644 doc/image/rrv64_noc_intf_to_soc.svg create mode 100644 doc/image/rrv64_noc_router_intf.svg create mode 100644 doc/noc_intf.md create mode 100644 doc/noc_spec.md create mode 100644 doc/router_spec.md create mode 100644 doc/system_memory_map.md create mode 100755 env/sourceme create mode 100755 flow/lint/Makefile create mode 100755 flow/lint/rvh_core.prj create mode 100755 flow/lint/spyglass-run-design_read.tcl create mode 100755 flow/lint/spyglass-run-lint_lint_rtl.tcl create mode 100644 flow/syn/Makefile create mode 100644 flow/syn/tcl_scripts/assert.tcl create mode 100644 flow/syn/tcl_scripts/constraints.sdc create mode 100644 flow/syn/tcl_scripts/file_to_list.tcl create mode 100644 flow/syn/tcl_scripts/parse_arg.tcl create mode 100644 flow/syn/tcl_scripts/synth.tcl create mode 100644 flow/syn/tcl_scripts/synth_init_lib.tcl create mode 100755 flow/syn/tcl_scripts/synth_init_library.t12.tcl create mode 100755 rtl/include/rvh_noc_pkg.sv create mode 100644 rtl/input_port.sv create mode 100644 rtl/input_port_flit_decoder.md create mode 100644 rtl/input_port_flit_decoder.sv create mode 100644 rtl/input_port_vc.sv create mode 100644 rtl/input_to_output.sv create mode 100644 rtl/local_port_couple_module.sv create mode 100644 rtl/local_port_look_adead_routing.sv create mode 100644 rtl/look_adead_routing.sv create mode 100755 rtl/model/cells/rrv64_cell_clkgate.v create mode 100755 rtl/model/cells/std_dff.sv create mode 100755 rtl/model/cells/std_dffe.sv create mode 100755 rtl/model/cells/std_dffr.sv create mode 100755 rtl/model/cells/std_dffre.sv create mode 100755 rtl/model/cells/std_dffrve.sv create mode 100644 rtl/model/simple_dual_one_clock.v create mode 100644 rtl/output_port_vc_assignment.sv create mode 100644 rtl/output_port_vc_credit_counter.sv create mode 100644 rtl/output_port_vc_selection.sv create mode 100644 rtl/performance_monitor.sv create mode 100644 rtl/priority_req_select.sv create mode 100755 rtl/ruby/ut_lib.sv create mode 100644 rtl/sa_global.sv create mode 100644 rtl/sa_local.sv create mode 100644 rtl/switch.sv create mode 100755 rtl/util/age_order_selector.sv create mode 100755 rtl/util/age_order_selector_with_head.sv create mode 100644 rtl/util/commoncell/.gitmodules create mode 100644 rtl/util/commoncell/CommonCell.yaml create mode 100644 rtl/util/commoncell/README.md create mode 100644 rtl/util/commoncell/doc/.gitignore create mode 100644 rtl/util/commoncell/env/sourceme create mode 100644 rtl/util/commoncell/src/Basic/Basic.yaml create mode 100644 rtl/util/commoncell/src/Basic/dv/CountOne_tb.v create mode 100644 rtl/util/commoncell/src/Basic/dv/MuxOH_tb.v create mode 100644 rtl/util/commoncell/src/Basic/dv/PLRU_tb.v create mode 100644 rtl/util/commoncell/src/Basic/dv/StreamFIFO_tb.v create mode 100644 rtl/util/commoncell/src/Basic/dv/SyncFIFO_tb.v create mode 100644 rtl/util/commoncell/src/Basic/hw/CountOne.v create mode 100644 rtl/util/commoncell/src/Basic/hw/MuxOH.v create mode 100644 rtl/util/commoncell/src/Basic/hw/OH2UInt.v create mode 100644 rtl/util/commoncell/src/Basic/hw/PLRU.v create mode 100644 rtl/util/commoncell/src/Basic/hw/PLRUTree.v create mode 100644 rtl/util/commoncell/src/Basic/hw/PriorityMux.v create mode 100644 rtl/util/commoncell/src/Basic/hw/StreamFIFO.v create mode 100644 rtl/util/commoncell/src/Basic/hw/SyncFIFO.v create mode 100644 rtl/util/commoncell/src/Queue/Queue.yaml create mode 100644 rtl/util/commoncell/src/Queue/dv/AgeMatrixSelector_tb.v create mode 100644 rtl/util/commoncell/src/Queue/dv/FIFO/MultiPortStreamFIFO_tb.v create mode 100644 rtl/util/commoncell/src/Queue/dv/StaticPrioritySelector_tb.v create mode 100644 rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v create mode 100644 rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithAgeMatrix.v create mode 100644 rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithQueueManager.v create mode 100644 rtl/util/commoncell/src/Queue/hw/FIFO/MultiPortStreamFIFO.v create mode 100644 rtl/util/commoncell/src/Queue/hw/QueueManager.v create mode 100644 rtl/util/commoncell/src/Queue/hw/StaticPrioritySelector.v create mode 100644 rtl/util/commoncell/src/StdDFF/StdDFF.yaml create mode 100644 rtl/util/commoncell/src/StdDFF/dv/DFFUncertainChecker.v create mode 100644 rtl/util/commoncell/src/StdDFF/hw/DFF.v create mode 100644 rtl/util/commoncell/src/StdDFF/hw/DFFE.v create mode 100644 rtl/util/commoncell/src/StdDFF/hw/DFFR.v create mode 100644 rtl/util/commoncell/src/StdDFF/hw/DFFRE.v create mode 100644 rtl/util/commoncell/tools/pico/.gitignore create mode 100755 rtl/util/commoncell/tools/pico/PackageParser.py create mode 100644 rtl/util/commoncell/tools/pico/README.md create mode 100644 rtl/util/commoncell/tools/pico/defaultToolOption.yaml create mode 100755 rtl/util/commoncell/tools/pico/pico create mode 100755 rtl/util/freelist.sv create mode 100644 rtl/util/inorder_router.sv create mode 100644 rtl/util/left_circular_rotate.sv create mode 100755 rtl/util/mp_fifo.sv create mode 100755 rtl/util/mp_fifo_ptr_output.sv create mode 100755 rtl/util/mp_freelist.sv create mode 100644 rtl/util/oh2idx.sv create mode 100755 rtl/util/one_counter.sv create mode 100755 rtl/util/one_hot_priority_encoder.sv create mode 100755 rtl/util/one_hot_rr_arb.sv create mode 100755 rtl/util/onehot_mux.sv create mode 100644 rtl/util/plru.sv create mode 100755 rtl/util/priority_encoder.sv create mode 100755 rtl/util/pseudo_dual_ram.sv create mode 100755 rtl/util/pseudo_ram.sv create mode 100755 rtl/util/reg_dual_ram.sv create mode 100755 rtl/util/select_two_from_n_valid.sv create mode 100755 rtl/util/sp_fifo_dat_vld_output.sv create mode 100755 rtl/util/usage_manager.sv create mode 100644 rtl/vnet_router.sv create mode 100644 tb/Makefile create mode 100644 tb/flist_mesh.f create mode 100644 tb/flist_mesh.syn.f create mode 100644 tb/flist_single_router.f create mode 100644 tb/flist_single_router.syn.f create mode 100644 tb/tb_mesh.sv create mode 100644 tb/tb_single_router.sv create mode 100644 tb/testbench.sv create mode 100644 tb/top_mesh_syn.sv create mode 100644 tb/top_single_router_syn.sv create mode 100644 tb/v_noc_pkg.sv create mode 100644 tb/v_receiver.sv create mode 100644 tb/v_scoreboard.sv create mode 100644 tb/v_sender.sv create mode 100644 tb/v_test_generator.sv diff --git a/doc/image/router_microarchitecture.drawio.png b/doc/image/router_microarchitecture.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..19766698807d21cb4f1df1e064dfef77b814a2b5 GIT binary patch literal 102035 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 zZ+xW>!&WQp+>ytP;KlLXfPHzNc9;~(Sbtd^B(aXjzRrrv64_noc_intf_to_socnode_id_y_ths_hop_inode_id_x_ths_hop_itx_flit_rec_ready_local_port_0rx_flit_local_port_0rx_flit_v_local_port_0rx_flit_pend_local_port_0rstnclkrx_flit_send_ready_local_port_0tx_flit_pend_local_port_0tx_flit_v_local_port_0tx_flit_local_port_0 \ No newline at end of file diff --git a/doc/image/rrv64_noc_router_intf.svg b/doc/image/rrv64_noc_router_intf.svg new file mode 100644 index 0000000..cec3949 --- /dev/null +++ b/doc/image/rrv64_noc_router_intf.svg @@ -0,0 +1 @@ +rrv64_noc_router_intfnode_id_y_ths_hop_inode_id_x_ths_hop_itx_lcrd_id_itx_lcrd_v_irx_flit_look_ahead_routing_irx_flit_vc_id_irx_flit_irx_flit_v_irx_flit_pend_itx_flit_rec_ready_local_port_0rx_flit_local_port_0rx_flit_v_local_port_0rx_flit_pend_local_port_0rstnclkrx_flit_send_ready_local_port_0tx_flit_pend_local_port_0tx_flit_v_local_port_0tx_flit_local_port_0tx_flit_pend_otx_flit_v_otx_flit_otx_flit_vc_id_otx_flit_look_ahead_routing_orx_lcrd_v_orx_lcrd_id_o \ No newline at end of file diff --git a/doc/noc_intf.md b/doc/noc_intf.md new file mode 100644 index 0000000..8f3d0a9 --- /dev/null +++ b/doc/noc_intf.md @@ -0,0 +1,59 @@ +# 1 NoC to local device and SoC intf + +## 1.1 Ports + +| Name | Direction | Type | Description | +| :------------------------------ | :-------- | :------------------------------------ | :------------------------------- | +| clk | in | wire logic | | +| rstn | in | wire logic | | +| rx_flit_pend_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | local port input | +| rx_flit_v_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | local port input | +| rx_flit_local_port_0 | in | wire [CHANNEL_NUM-1:0] flit_payload_t | local port input | +| rx_flit_send_ready_local_port_0 | out | [CHANNEL_NUM-1:0] logic | credit based flow control signal | +| tx_flit_pend_local_port_0 | out | [CHANNEL_NUM-1:0] logic | local port output | +| tx_flit_v_local_port_0 | out | [CHANNEL_NUM-1:0] logic | local port output | +| tx_flit_local_port_0 | out | [CHANNEL_NUM-1:0] flit_payload_t | local port output | +| tx_flit_rec_ready_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | credit based flow control signal | +| node_id_x_ths_hop_i | in | wire [NodeID_X_Width-1:0] logic | router position | +| node_id_y_ths_hop_i | in | wire [NodeID_Y_Width-1:0] logic | router position | + +## 1.2 Diagram + +![rrv64_router_intf](./image/rrv64_noc_intf_to_soc.svg) + +# 2 NoC Router intf + +## 2.1 Ports + +| Name | Direction | Type | Description | +| :------------------------------ | :-------- | :-------------------------------------------------------------------------- | :------------------------------- | +| clk | in | wire logic | | +| rstn | in | wire logic | | +| rx_flit_pend_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | local port input | +| rx_flit_v_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | local port input | +| rx_flit_local_port_0 | in | wire [CHANNEL_NUM-1:0] flit_payload_t | local port input | +| rx_flit_send_ready_local_port_0 | out | [CHANNEL_NUM-1:0] logic | credit based flow control signal | +| tx_flit_pend_local_port_0 | out | [CHANNEL_NUM-1:0] logic | local port output | +| tx_flit_v_local_port_0 | out | [CHANNEL_NUM-1:0] logic | local port output | +| tx_flit_local_port_0 | out | [CHANNEL_NUM-1:0] flit_payload_t | local port output | +| tx_flit_rec_ready_local_port_0 | in | wire [CHANNEL_NUM-1:0] logic | credit based flow control signal | +| rx_flit_pend_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | router port input | +| rx_flit_v_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | router port input | +| rx_flit_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] flit_payload_t | router port input | +| rx_flit_vc_id_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] [VC_ID_NUM_MAX_W-1:0] logic | router port input | +| rx_flit_look_ahead_routing_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] rvh_noc_pkg::io_port_t | router port input | +| tx_flit_pend_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | router port output | +| tx_flit_v_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | router port output | +| tx_flit_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] flit_payload_t | router port output | +| tx_flit_vc_id_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] [VC_ID_NUM_MAX_W-1:0] logic | router port output | +| tx_flit_look_ahead_routing_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] rvh_noc_pkg::io_port_t | router port output | +| rx_lcrd_v_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | credit based flow control signal | +| rx_lcrd_id_o | out | [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] [VC_ID_NUM_MAX_W-1:0] logic | credit based flow control signal | +| tx_lcrd_v_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] logic | credit based flow control signal | +| tx_lcrd_id_i | in | wire [CHANNEL_NUM-1:0] [ROUTER_PORT_NUMBER-1:0] [VC_ID_NUM_MAX_W-1:0] logic | credit based flow control signal | +| node_id_x_ths_hop_i | in | wire [NodeID_X_Width-1:0] logic | router position | +| node_id_y_ths_hop_i | in | wire [NodeID_Y_Width-1:0] logic | router position | + +## 2.2 Diagram + +![rrv64_noc_router_intf](./image/rrv64_noc_router_intf.svg) diff --git a/doc/noc_spec.md b/doc/noc_spec.md new file mode 100644 index 0000000..dbc78ba --- /dev/null +++ b/doc/noc_spec.md @@ -0,0 +1,538 @@ +# NoC Spec + +## 1 Interface +This NoC uses CHI compatible interface. It defines 4 channels for different kinds of messages. Each channel is implemented in a specific physical sub-networks, so the NoC has 4 sub-networks. +Each router can be configured to have 0~4 local port(s). + +### 1.1 Channel + +In this specification, the Link layer provides a set of channels for flit communication. + +Each channel has a defined flit format that has multiple fields and some of the field widths have multiple possible values. In some cases, the defined flit format can be used on both an inbound and an outbound channel. + +Table 1 shows the channels, and the mapping onto the Request Node, Home Node and Subordinate Node component channels. + +| Channel | Description | Usage | RN Channel | HN Channel | SN Channel | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------- | ---------- | ---------- | +| REQ | The request channel transfers flits associated with request messages such as Read requests and Write requests. | Requests from RN to HN | TXREQ | RXREQ | - | +| | | Requests from RN to SN | TXREQ | - | RXREQ | +| | | Requests from HN to SN | - | TXREQ | RXREQ | +| RSP | The response channel transfers flits associated with response messages that do not have a data payload such as write completion messages. | Responses from SN to HN | - | RXRSP | TXRSP | +| | | Responses from SN to RN | RXRSP | | TXRSP | +| | | Responses from HN to RN | RXRSP | TXRSP | - | +| | | Snoop Response and Completion Acknowledge from snoopee RN to HN | TXRSP | RXRSP | - | +| SNP | The snoop channel transfers flits associated with Snoop Request messages. | Snoop Requests from HN to snoopee RN | RXSNP | TXSNP | - | +| DAT | The data channel transfers flits associated with protocol messages that have a data payload such as read completion and WriteData messages. | WriteData, and Snoop response data from an RN to HN | TXDAT | RXDAT | - | +| | | WriteData from an RN to SN | TXDAT | - | RXDAT | +| | | WriteData from an HN to SN | - | TXDAT | RXDAT | +| | | Read data from SN to RN | RXDAT | - | TXDAT | +| | | Read data from SN to HN | - | RXDAT | TXDAT | +| | | Read data from HN to RN | RXDAT | TXDAT | - | + +Table 2 shows the channels of Request Node, Home Node and Subordinate Node component have. + +| Component | TXREQ | RXREQ | TXRSP | RXRSP | TXSNP | RXSNP | TXDAT | RXDAT | +| --------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | +| RN | Y | - | Y | Y | - | Y | Y | Y | +| HN | Y | Y | Y | Y | Y | - | Y | Y | +| SN | - | Y | Y | - | - | - | Y | Y | + +// TODO: RN need RXREQ to receive core to core interrupt + +### 1.2 Port + +Table 3 shows the links of each router (and channel) port. + +| Name | Direction | Type | Description | +| :--------------------------- | :-------- | :----------------------------------------------------- | :------------------------------------------------- | +| rx_flit_pend_i | in | wire [INPUT_PORT_NUM-1:0] logic | input from other router or local port // N,S,E,W,L | +| rx_flit_v_i | in | wire [INPUT_PORT_NUM-1:0] logic | | +| rx_flit_i | in | wire [INPUT_PORT_NUM-1:0] flit_payload_t | | +| rx_flit_vc_id_i | in | wire [INPUT_PORT_NUM-1:0] [VC_ID_NUM_MAX_W-1:0] logic | | +| rx_flit_look_ahead_routing_i | in | wire [INPUT_PORT_NUM-1:0] rvh_noc_pkg::io_port_t | | +| tx_flit_pend_o | out | [OUTPUT_PORT_NUM-1:0] logic | output to other router or local port // N,S,E,W,L | +| tx_flit_v_o | out | [OUTPUT_PORT_NUM-1:0] logic | | +| tx_flit_o | out | [OUTPUT_PORT_NUM-1:0] flit_payload_t | | +| tx_flit_vc_id_o | out | [OUTPUT_PORT_NUM-1:0] [VC_ID_NUM_MAX_W-1:0] logic | | +| tx_flit_look_ahead_routing_o | out | [OUTPUT_PORT_NUM-1:0] rvh_noc_pkg::io_port_t | | +| rx_lcrd_v_o | out | [INPUT_PORT_NUM-1:0] logic | free VC credit sent to sender | +| rx_lcrd_id_o | out | [INPUT_PORT_NUM-1:0] [VC_ID_NUM_MAX_W-1:0] logic | | +| tx_lcrd_v_i | in | wire [OUTPUT_PORT_NUM-1:0] logic | free VC credit received from receiver | +| tx_lcrd_id_i | in | wire [OUTPUT_PORT_NUM-1:0] [VC_ID_NUM_MAX_W-1:0] logic | | +| node_id_x_ths_hop_i | in | wire [NodeID_X_Width-1:0] logic | router addr | +| node_id_y_ths_hop_i | in | wire [NodeID_Y_Width-1:0] logic | | +| clk | in | wire logic | | +| rstn | in | wire logic | | + +### 1.3 Flit packet definitions + +Refer to IHI0050F_amba_chi_architecture_spec Flit packet definitions. This part illustrates the definition of the variable length portion of the CHI flit for this design. +#### 1.3.1 Request flit + +| Field | Field width | Comment | +| ----------------------- | --------------------------------- | ----------------------------------------- | +| QoS | 4 | - | +| TgtID | 7 | Width determined by NodeID_Width | +| SrcID | 7 | Width determined by NodeID_Width | +| TxnID | 12 | - | +| ReturnNID or | 7 | Used for DMT | +| StashNID or | | Used in Stash transactions | +| {(NodeID_Width - 7)'b0, | | SBZ | +| SLCRepHint[6:0]} | | Used in cache line replacement algorithms | +| StashNIDValid | 1 | Used in Stash transactions | +| Endian | | Used in Atomic transactions | +| Deep | | Used in CleanSharedPersist* transactions | +| ReturnTxnID[11:0] | 12 | Used for DMT | +| {6'b0, | | SBZ | +| StashLPIDValid, | | Used in Stash transactions | +| StashLPID[4:0]} | | Used in Stash transactions | +| Opcode | 7 | - | +| Size | 3 | - | +| Addr | RAW = 44 to 52 | Width determined by Req_Addr_Width (RAW) | +| NS | 1 | - | +| NSE | 1 | - | +| LikelyShared | 1 | - | +| AllowRetry | 1 | - | +| Order | 2 | - | +| PCrdType | 4 | - | +| MemAttr | 4 | - | +| SnpAttr or | 1 | - | +| DoDWT | | Used for DWT | +| PGroupID[7:0] or | 8 | Used in Persistent CMO transactions | +| StashGroupID[7:0] or | | Used in the StashOnceSep transaction | +| TagGroupID[7:0] or | | Used for Memory Tagging | +| {3'b0, | | SBZ | +| LPID[4:0]} | | - | +| Excl | 1 | Used in Exclusive transactions | +| SnoopMe | | Used in Atomic transactions | +| CAH | | Used in CopyBack Write transactions | +| ExpCompAck | 1 | - | +| TagOp | 2 | - | +| TraceTag | 1 | - | +| MPAM | M = 0 | No MPAM bus | +| | M = 12 | - | +| PBHA | PB = 0 | No PBHA bus | +| | PB = 4 | - | +| RSVDC | Y = 0 | No RSVDC bus | +| | Y = 4, 8, 12, 16, 24, 32 | - | +| Total | R = (88 + RAW + Y + M + PB) = 132 | RAW = 44, Y = 0, M = 0, PB = 0 | + + +#### 1.3.2 Response flit + +| Field | Field width | Comment | +| --------------------- | ----------- | ----------------------------------- | +| QoS | 4 | - | +| TgtID | 7 | Width determined by NodeID_Width | +| SrcID | 7 | Width determined by NodeID_Width | +| TxnID | 12 | - | +| Opcode | 5 | - | +| RespErr | 2 | - | +| Resp | 3 | - | +| FwdState[2:0] or | 3 | Used for DCT | +| DataPull[2:0] | | Used in Stash transactions | +| CBusy | 3 | - | +| DBID[11:0] or | 12 | - | +| {4'b0, | | SBZ | +| PGroupID[7:0]} or | | Used in Persistent CMO transactions | +| {4'b0, | | SBZ | +| StashGroupID[7:0]} or | | Used in Stash transactions | +| {4'b0, | | SBZ | +| TagGroupID[7:0]} | | Used for Memory Tagging | +| PCrdType | 4 | - | +| TagOp | 2 | - | +| TraceTag | 1 | - | +| Total | T = 65 | - | + +#### 1.3.3 Snoop flit + +| Field | Field width | Comment | +| ---------------------- | ----------------------- | -------------------------------- | +| QoS | 4 | - | +| SrcID | 7 | Width determined by NodeID_Width | +| TxnID | 12 | - | +| FwdNID or | 7 | Width determined by NodeID_Width | +| {(NodeID_Width - 4)'0, | | | +| PBHA[3:0]} | | | +| FwdTxnID[11:0] or | 12 | Used for DCT | +| {6'b0, | | SBZ | +| StashLPIDValid | | Used in Stash transactions | +| StashLPID[4:0]} or | | Used in Stash transactions | +| {4'b0, | | SBZ | +| VMIDExt[7:0]} | | Used in DVM transactions | +| Opcode | 5 | - | +| Addr | SAW = 41 to 49 | Req_Addr_Width - 3 | +| NS | 1 | - | +| NSE | 1 | - | +| DoNotGoToSD | 1 | - | +| RetToSrc | 1 | - | +| TraceTag | 1 | - | +| MPAM | M = 0 | No MPAM bus | +| | M = 11 | - | +| Total | S = (52 + SAW + M) = 93 | SAW = 41, M = 0 | + +#### 1.3.4 Data flit + +| Field | Field width | Comment | +| ----------------------- | ----------------------------- | ----------------------------------- | +| QoS | 4 | - | +| TgtID | 7 | Width determined by NodeID_Width | +| SrcID | 7 | Width determined by NodeID_Width | +| TxnID | 12 | - | +| HomeNID or | 7 | Width determined by NodeID_Width | +| {(NodeID_Width - 4)'b0, | | | +| PBHA[3:0]} | | | +| Opcode | 4 | - | +| RespErr | 2 | - | +| Resp | 3 | - | +| DataSource[4:0] or | 5 | Indicates Data source in a response | +| {2'b0, | | SBZ | +| FwdState[2:0]} or | | Used for DCT | +| {2'b0, | | SBZ | +| DataPull[2:0]} | | Used in Stash transactions | +| CBusy | 3 | - | +| DBID[11:0] | 12 | - | +| CCID | 2 | - | +| DataID | 2 | - | +| TagOp | 2 | - | +| Tag | DW/32 = 4, 8, 16 | - | +| TU | DW/128 = 1, 2, 4 | - | +| TraceTag | 1 | - | +| CAH | 1 | - | +| RSVDC | Y = 0 | No RSVDC bus | +| | Y = 4, 8, 12, 16, 24, 32 | - | +| BE | DW/8 = 16, 32, 64 | - | +| Data | DW = 128, 256, 512 | DW = Data bus width | +| DataCheck (DC) | 0 or DW/8 = 16, 32, 64 | - | +| Poison (P) | 0 or DW/64 = 2, 4, 8 | - | +| Total | D = (223 to 235) + Y + DC + P | DW = 128 bit Data | +| | D = (372 to 384) + Y + DC + P | DW = 256 bit Data | +| | D = (670 to 682) + Y + DC + P | DW = 512 bit Data | +| | D = 223 + Y + DC + P = 223 | DW = 128, Y = 0, DC = 0, P = 0 | +| | D = 372 + Y + DC + P = 372 | DW = 128, Y = 0, DC = 0, P = 0 | + + + + + +## 2 Parameters and Configuations + +There are some parameters and configuations to set. + +### 2.1 NoC Configuations + +Table 4 shows configuations for overall NoC. + +| Name | Type | Default Value | Description | +| :----------------------------- | :----- | :------------ | :------------------------------------------------------------------------------------ | +| HAVE_LOCAL_PORT | define | defined | The router have local port(s) | +| LOCAL_PORT_NUM_2 | define | undefined | The router have >= 2 local ports | +| LOCAL_PORT_NUM_3 | define | undefined | The router have >= 3 local ports | +| LOCAL_PORT_NUM_4 | define | undefined | The router have 4 local ports | +| VC_DATA_USE_DUAL_PORT_RAM | define | undefined | Use unified dual-port ram per input port VC data buffer (default: separate dff fifo) | +| RETURN_CREDIT_AT_SA_STAGE | define | undefined | Reture credit to send at sa stage rather than st atage | +| ALLOW_SAME_ROUTER_L2L_TRANSFER | define | undefined | Allow local ports in same router transfer flit, at least 2 local ports | +| COMMON_QOS | define | undefined | QoS, no special VC, all VC head flits ranked by QoS value | +| COMMON_QOS_EXTRA_RT_VC | define | defined | QoS, add special VC for highest priority flits, all VC head flits ranked by QoS value | + +### 2.2 NoC Parameters + +Table 5 shows parameters for overall NoC. + +| Name | Type | Default Value | Description | +| :------------------------ | :--- | :---------------------------------------------------------------------------------- | :------------------------------------------- | +| CHANNEL_NUM | int | 4 | 4 channels: req, resp, data, snp | +| NodeID_X_Width | int | 2 | | +| NodeID_Y_Width | int | 3 | | +| NodeID_Device_Port_Width | int | 2 | | +| NodeID_Device_Id_Width | int | 1 | | +| NodeID_Width | int | NodeID_X_Width + NodeID_Y_Width + NodeID_Device_Port_Width + NodeID_Device_Id_Width | | +| TxnID_Width | int | 12 | | +| QoS_Value_Width | int | 4 | | +| FLIT_LENGTH | int | 256 | per channel refer to flit definitation | +| INPUT_PORT_NUMBER | int | 5 | | +| INPUT_PORT_NUMBER_IDX_W | int | INPUT_PORT_NUMBER > 1 ? $clog2(INPUT_PORT_NUMBER) : 1 | | +| OUTPUT_PORT_NUMBER | int | 5 | | +| ROUTER_PORT_NUMBER | int | 4 | 4 ports connect to other routers: N, S, E, W | +| LOCAL_PORT_NUMBER | int | INPUT_PORT_NUMBER-4 | Ports connect to local device(s) | +| QOS_VC_NUM_PER_INPUT | int | 1 | | +| VC_ID_NUM_MAX | int | (CHANNEL_NUM-1)+LOCAL_PORT_NUMBER+QOS_VC_NUM_PER_INPUT | | +| VC_ID_NUM_MAX_W | int | VC_ID_NUM_MAX > 1 ? $clog2(VC_ID_NUM_MAX) : 1 | | +| SA_GLOBAL_INPUT_NUM_MAX | int | (CHANNEL_NUM-1)+LOCAL_PORT_NUMBER | | +| SA_GLOBAL_INPUT_NUM_MAX_W | int | SA_GLOBAL_INPUT_NUM_MAX > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_MAX) : 1 | | +| VC_DEPTH_MAX | int | 4 | | +| VC_DPRAM_DEPTH_MAX | int | VC_ID_NUM_MAX * VC_DEPTH_MAX | | +| VC_BUFFER_DEPTH_MAX_W | int | VC_DEPTH_MAX > 1 ? $clog2(VC_DEPTH_MAX) : 1 | | +| VC_DPRAM_DEPTH_MAX_W | int | VC_DPRAM_DEPTH_MAX > 1 ? $clog2(VC_DPRAM_DEPTH_MAX) : 1 | | + + +### 2.3 Router Parameters + +Table 6 shows parameters for each router instance, the default values are likely to be overwrite by NoC parameters. + +| Name | Type | Default Value | Description | +| :-------------------------- | :------------- | :------------------------------------------------------------ | :---------------------- | +| INPUT_PORT_NUM | int | 5 | | +| OUTPUT_PORT_NUM | int | 5 | | +| LOCAL_PORT_NUM | int | INPUT_PORT_NUM-4 | | +| flit_payload_t | flit_payload_t | logic[256-1:0] | | +| QOS_VC_NUM_PER_INPUT | int | 0 | | +| VC_NUM_INPUT_N | int | 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_INPUT_S | int | 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_INPUT_E | int | 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_INPUT_W | int | 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_INPUT_L | int | 4+LOCAL_PORT_NUM-1+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_INPUT_N_IDX_W | int | VC_NUM_INPUT_N > 1 ? $clog2(VC_NUM_INPUT_N) : 1 | | +| VC_NUM_INPUT_S_IDX_W | int | VC_NUM_INPUT_S > 1 ? $clog2(VC_NUM_INPUT_S) : 1 | | +| VC_NUM_INPUT_E_IDX_W | int | VC_NUM_INPUT_E > 1 ? $clog2(VC_NUM_INPUT_E) : 1 | | +| VC_NUM_INPUT_W_IDX_W | int | VC_NUM_INPUT_W > 1 ? $clog2(VC_NUM_INPUT_W) : 1 | | +| VC_NUM_INPUT_L_IDX_W | int | VC_NUM_INPUT_L > 1 ? $clog2(VC_NUM_INPUT_L) : 1 | | +| SA_GLOBAL_INPUT_NUM_N | int | 3+LOCAL_PORT_NUM | | +| SA_GLOBAL_INPUT_NUM_S | int | 3+LOCAL_PORT_NUM | | +| SA_GLOBAL_INPUT_NUM_E | int | 1+LOCAL_PORT_NUM | | +| SA_GLOBAL_INPUT_NUM_W | int | 1+LOCAL_PORT_NUM | | +| SA_GLOBAL_INPUT_NUM_L | int | 4+LOCAL_PORT_NUM-1 | | +| SA_GLOBAL_INPUT_NUM_N_IDX_W | int | SA_GLOBAL_INPUT_NUM_N > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_N) : 1 | | +| SA_GLOBAL_INPUT_NUM_S_IDX_W | int | SA_GLOBAL_INPUT_NUM_S > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_S) : 1 | | +| SA_GLOBAL_INPUT_NUM_E_IDX_W | int | SA_GLOBAL_INPUT_NUM_E > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_E) : 1 | | +| SA_GLOBAL_INPUT_NUM_W_IDX_W | int | SA_GLOBAL_INPUT_NUM_W > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_W) : 1 | | +| SA_GLOBAL_INPUT_NUM_L_IDX_W | int | SA_GLOBAL_INPUT_NUM_L > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_L) : 1 | | +| VC_NUM_OUTPUT_N | int | 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_OUTPUT_S | int | 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_OUTPUT_E | int | 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_OUTPUT_W | int | 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT | | +| VC_NUM_OUTPUT_L | int | 1 | VC number in local node | +| VC_NUM_OUTPUT_N_IDX_W | int | VC_NUM_OUTPUT_N > 1 ? $clog2(VC_NUM_OUTPUT_N) : 1 | | +| VC_NUM_OUTPUT_S_IDX_W | int | VC_NUM_OUTPUT_S > 1 ? $clog2(VC_NUM_OUTPUT_S) : 1 | | +| VC_NUM_OUTPUT_E_IDX_W | int | VC_NUM_OUTPUT_E > 1 ? $clog2(VC_NUM_OUTPUT_E) : 1 | | +| VC_NUM_OUTPUT_W_IDX_W | int | VC_NUM_OUTPUT_W > 1 ? $clog2(VC_NUM_OUTPUT_W) : 1 | | +| VC_NUM_OUTPUT_L_IDX_W | int | VC_NUM_OUTPUT_L > 1 ? $clog2(VC_NUM_OUTPUT_L) : 1 | | +| VC_DEPTH_INPUT_N | int | 2 | | +| VC_DEPTH_INPUT_S | int | 2 | | +| VC_DEPTH_INPUT_E | int | 2 | | +| VC_DEPTH_INPUT_W | int | 2 | | +| VC_DEPTH_INPUT_L | int | 2 | | +| VC_DEPTH_OUTPUT_N | int | VC_DEPTH_INPUT_N | | +| VC_DEPTH_OUTPUT_S | int | VC_DEPTH_INPUT_S | | +| VC_DEPTH_OUTPUT_E | int | VC_DEPTH_INPUT_E | | +| VC_DEPTH_OUTPUT_W | int | VC_DEPTH_INPUT_W | | +| VC_DEPTH_OUTPUT_L | int | VC_DEPTH_INPUT_L | | +| VC_DEPTH_OUTPUT_N_COUNTER_W | int | $clog2(VC_DEPTH_OUTPUT_N + 1) | | +| VC_DEPTH_OUTPUT_S_COUNTER_W | int | $clog2(VC_DEPTH_OUTPUT_S + 1) | | +| VC_DEPTH_OUTPUT_E_COUNTER_W | int | $clog2(VC_DEPTH_OUTPUT_E + 1) | | +| VC_DEPTH_OUTPUT_W_COUNTER_W | int | $clog2(VC_DEPTH_OUTPUT_W + 1) | | +| VC_DEPTH_OUTPUT_L_COUNTER_W | int | $clog2(VC_DEPTH_OUTPUT_L + 1) | | + + +## 3 Topology + +Configurable 2D mesh topology, the default topology in `tb_mesh.sv` and `top_mesh_syn.sv` is a 3x3 2D mesh. + +``` + NoC Topology + + (0,2) -- (1,2) -- (2,2) + | \ | \ | \ + | rn5 | rn6 | rn7 + (0,1) -- (1,1) -- (2,1) + | \ | \ | \ + | rn2 | rn3 | rn4 + (0,0) -- (1,0) -- (2,0) + \ \ \ + rn0 hn0 rn1 +``` + +## 4 Routing + +Use X-Y routing: + +Routing directions are referred to by the mesh port that the NoC routes the flit through. For +example, if the NoC routes the flit northwards, then the flit is sent through the north mesh port. + +If there is a mismatch between the target NoC XID and the current NoC XID, then the NoC uses +the following rule to decide the routing direction: + + * If target NoC XID > current NoC XID, then route eastwards + * Otherwise, route westwards + +If the target NoC XID and the current NoC XID match, then the flit routing components are +compared against the YID of the NoC. If YIDs do not match, then the NoC uses the following rule +to decide the routing direction: + + * If target NoC YID > current NoC YID, then route northwards + * Otherwise, route southwards + +If the target NoC XID and YID match the current NoC XID and YID, then the flit has reached the +target NoC. At this point, the flit is downloaded to the target device. + +## 5 Flow Control + +A flow-control mechanism by which the transmitting device uses link layer credits to send +NoC flits – one credit per flit. In turn, the receiving device sends these credits back to the +transmitting device, one at a time, when it is done processing each flit to allow for subsequent +flit transfers. + +## 6 Router Microarchitecture + +![alt router_microarchitecture](./image/router_microarchitecture.drawio.png) + +Features: +* 2-stage pipeline. +* credited-based flow control. +* Vitrual channel(VC) for QoS and switch allocation efficiency. +* The allocation priority of VC is based on look-ahead routing result. +* 2-level switch allocation, based on fair round-robin algorithm. +* Look-ahead routing for next hop router for better timing and VC allocation. +* Decoupled VC selection from VC allocation for better timing. + +### 6.1 Routing Algorithm +Use XY routing, the routing result is calculated one hop ahead, which is look-ahead routing + +#### 6.1.1 XY Routing +Flits firstly go through X axis, then go though Y axis: +``` + 1st phase: Assign next address + 2nd phase: Define new Next-port +``` +#### 6.1.2 Look-ahead Routing + +The routing result is calculated at last hop router or local onput port, to achieve better timing and VC allocation. + +### 6.2 Input Port + +#### 6.2.1 Input VC + +1. Use Fixed VC Assignment with Dynamic VC Allocation (FVADA) [paper](https://sites.pitt.edu/~juy9/papers/Yi-HPCA10.pdf) VC allocation mechanism. The input VCs are associated with the flits' output port. +2. XY routing, so some of the output port are not used in some input ports. + +* VC in each input port: + +| input port | input port id | VC id | priority be assigned output port | output port id | +| ---------- | ------------- | ----- | -------------------------------- | -------------- | +| N | 0 | 0 | S | 1 | +| | | 1 | L | 4 | +| S | 1 | 0 | N | 0 | +| | | 1 | L | 4 | +| E | 2 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | W | 3 | +| | | 3 | L | 4 | +| W | 3 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | E | 2 | +| | | 3 | L | 4 | +| L | 4 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | E | 2 | +| | | 3 | W | 3 | + + +### 6.3 Switch Allocation + +Use two-stage input-port-first allocation. + +#### 6.3.1 Local allocation +Use round-robin arbition. As XY routing, the local arbiters' input port number are not the same. + +#### 6.3.2 Global allocation + +Use round-robin arbition. As XY routing, the global arbiters' input port number are not the same. + +* input output port mapping for each SA global arbiter: + +| output port | global SA arbiter connected input ports | global SA arbiter connected input ports id | next hop input port | +| ----------- | --------------------------------------- | ------------------------------------------ | ------------------- | +| N | S | 1 | S | +| | E | 2 | | +| | W | 3 | | +| | L | 4 | | +| S | N | 0 | N | +| | E | 2 | | +| | W | 3 | | +| | L | 4 | | +| E | W | 3 | W | +| | L | 4 | | +| W | E | 2 | E | +| | L | 4 | | +| L | N | 0 | - | +| | S | 1 | | +| | E | 2 | | +| | W | 3 | | + + + +## 7 QoS Support + +Features: +1. Each flit supports a flit QoS value of 0-15, and the larger the value, the higher the priority. +2. Perform fair round robin arbitration on flits with the same priority. +3. If an input port is not sent out after several cycles by the flit selected by round robin, another flit with the same priority will participate in the arbitration, reducing head of line blocking and improving arbitration efficiency. +4. Support to put the flits that need the same router output port on the same virtual channel first, reduce head of line blocking, and improve arbitration efficiency. +5. Supports assigning a dedicated virtual channel to the flit with the highest QoS priority to ensure the lowest latency and can be used for real-time applications. +6. The QoS value of each flit is given by the request node when sending req. Currently it is a fixed value given according to the flit type. In the future, it is planned to support dynamic QoS value setting strategies based on transaction latency and requester throughput respectively. + +### 7.1 Common QoS + No special VC, all VC head flits ranked by QoS value. + * no extra real-time VC; + * no bypass arbiter for real-time VC; + * all VC involve QoS value compare. + +### 7.2 Common QoS + extra real-time VC QoS + Add special VC for highest priority flits, all VC head flits ranked by QoS value. + * have extra real-time VC; + * no bypass arbiter for real-time VC; + * the real-time VC always win the local sa, and the local sa rr point should not be updated + * the real-time VC join global sa as common QoS, as it has highest QoS value, it can beat flits with other QoS value; + * all VC involve QoS value compare. + + +## 8 System Memory Map (TODO) + +### 8.1 Home Node to Memory Channel + +#### 8.1.1 Range-based: non-hashed SN target ID + +use reg to direct assign a range of memory address to specific memory channel or io device + +#### 8.1.2 Range-based: hashed SN target ID + +##### 8.1.2.1 UMA mode + +use reg to assign a range of memory address, for 4 home node, 4 memory channel: +1. use PA[7:6] to interleave among memony channels (cache line interleave) +2. for unbalanced memory size among memory channels, use to smallest size channel to interleave, and other memory use direct assign + +| PA bit num | msb | ... | 8 | 7 | 6 | 5 - 0 | +| ----------------- | --- | --- | --- | --- | --- | ----------- | +| Interleaving type | | | | CH | CH | line offest | + +CH: Channel interleaving + +##### 8.1.2.2 NUMA mode + +use reg to assign a range of memory address, for 4 home node, 4 memory channel: +1. use PA[6] to interleave among memony channels (cache line interleave) +2. use PA[msb] to interleave among nodes +3. for unbalanced memory size among memory channels, use to smallest size channel to interleave, and other memory use direct assign + +| PA bit num | msb | ... | 8 | 7 | 6 | 5 - 0 | +| ----------------- | --- | --- | --- | --- | --- | ----------- | +| Interleaving type | ND | | | CH | CH | line offest | + +ND: Node interleaving, CH: Channel interleaving + +### 8.2 Request Node to Home Node + +#### 8.2.1 Non-hashed regions + +A given memory partition is assigned to an individual targetID (non-hashed), for: +1. PLIC(highest priority); +2. IO space; +3. Directly assigned memory range + +#### 8.2.2 Hashed memory region or non-hashed mode of hashed memory region + +##### 8.2.2.1 NUCA mode + +use reg to assign a range of memory address for one core/cluster to its nearest home node, +it can be configured at reset. diff --git a/doc/router_spec.md b/doc/router_spec.md new file mode 100644 index 0000000..27787b0 --- /dev/null +++ b/doc/router_spec.md @@ -0,0 +1,99 @@ +# Router Spec +# 1 Routing Algorithm +Use XY routing, the routing result is calculated one hop ahead, which is look-ahead routing + +## 1.1 XY Routing +Flits firstly go through X axis, then go though Y axis: +``` + 1st phase: Assign next address + 2nd phase: Define new Next-port +``` +## 1.2 Look-ahead Routing + +# 2 Input Port +## 2.1 Input VC + +1. Use Fixed VC Assignment with Dynamic VC Allocation (FVADA) [paper](https://sites.pitt.edu/~juy9/papers/Yi-HPCA10.pdf) vc allocation mechanism. The input VCs are associated with the flits' output port. +2. XY routing, so some of the output port are not used in some input ports. + +* VC in each input port: + +| input port | input port id | vc id | assigned output port | output port id | +| ---------- | ------------- | ----- | -------------------- | -------------- | +| N | 0 | 0 | S | 1 | +| | | 1 | L | 4 | +| S | 1 | 0 | N | 0 | +| | | 1 | L | 4 | +| E | 2 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | W | 3 | +| | | 3 | L | 4 | +| W | 3 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | E | 2 | +| | | 3 | L | 4 | +| L | 4 | 0 | N | 0 | +| | | 1 | S | 1 | +| | | 2 | E | 2 | +| | | 3 | W | 3 | + + +# 3 Switch Allocation + +Use two-stage input-port-first allocation. + +## 3.1 Local allocation +Use round robin arbition. As XY routing, the local arbiters' input port number are not the same. + +## 3.2 Global allocation + +Use round robin arbition. As XY routing, the global arbiters' input port number are not the same. + +* input output port mapping for each SA global arbiter: + +| output port | global SA arbiter connected input ports | global SA arbiter connected input ports id | next hop input port | +| ----------- | --------------------------------------- | ------------------------------------------ | ------------------- | +| N | S | 1 | S | +| | E | 2 | | +| | W | 3 | | +| | L | 4 | | +| S | N | 0 | N | +| | E | 2 | | +| | W | 3 | | +| | L | 4 | | +| E | W | 3 | W | +| | L | 4 | | +| W | E | 2 | E | +| | L | 4 | | +| L | N | 0 | - | +| | S | 1 | | +| | E | 2 | | +| | W | 3 | | + + + +# 4 QoS + +## 4.1 Common QoS + No special vc, all vc head flits ranked by QoS value. + * no extra real-time vc; + * no bypass arbiter for real-time vc; + * all vc involve QoS value compare. + +## 4.2 Common QoS + extra real-time vc QoS + Add special vc for highest priority flits, all vc head flits ranked by QoS value. + * have extra real-time vc; + * no bypass arbiter for real-time vc; + * the real-time vc always win the local sa, and the local sa rr point should not be updated + * the real-time vc join global sa as common QoS, as it has highest QoS value, it can beat flits with other QoS value; + * all vc involve QoS value compare. + +## 4.3 extra real-time vc QoS + bypass switch allocation + no common QoS + Add special vc for highest priority flits, other vc head flits have no QoS support. + * have extra real-time vc; + * have bypass arbiter for real-time vc; + * non-highest-priority vc have no QoS support. + * the real-time vc bypass local sa, and override common rr arbiter's result; + * the real-time vc from all possible inport use a separate rr arbiter at per global sa, and override common rr arbiter's result; + + It can have the same real-time support as 4.2, but may have better timing. diff --git a/doc/system_memory_map.md b/doc/system_memory_map.md new file mode 100644 index 0000000..c3472e1 --- /dev/null +++ b/doc/system_memory_map.md @@ -0,0 +1,50 @@ +# System Memory Map + +## 1 Home Node to Memory Channel + +### 1.1 Range-based: non-hashed SN target ID + +use reg to direct assign a range of memory address to specific memory channel or io device + +### 1.2 Range-based: hashed SN target ID + +#### 1.2.1 UMA mode + +use reg to assign a range of memory address, for 4 home node, 4 memory channel: +1. use PA[7:6] to interleave among memony channels (cache line interleave) +2. for unbalanced memory size among memory channels, use to smallest size channel to interleave, and other memory use direct assign + +| PA bit num | msb | ... | 8 | 7 | 6 | 5 - 0 | +| ----------------- | --- | --- | --- | --- | --- | ----------- | +| Interleaving type | | | | CH | CH | line offest | + +CH: Channel interleaving + +#### 1.2.2 NUMA mode + +use reg to assign a range of memory address, for 4 home node, 4 memory channel: +1. use PA[6] to interleave among memony channels (cache line interleave) +2. use PA[msb] to interleave among nodes +3. for unbalanced memory size among memory channels, use to smallest size channel to interleave, and other memory use direct assign + +| PA bit num | msb | ... | 8 | 7 | 6 | 5 - 0 | +| ----------------- | --- | --- | --- | --- | --- | ----------- | +| Interleaving type | ND | | | CH | CH | line offest | + +ND: Node interleaving, CH: Channel interleaving + +## 2 Request Node to Home Node + +### 2.1 Non-hashed regions + +A given memory partition is assigned to an individual targetID (non-hashed), for: +1. PLIC(highest priority); +2. IO space; +3. Directly assigned memory range + +### 2.2 Hashed memory region or non-hashed mode of hashed memory region + +#### 2.2.1 NUCA mode + +use reg to assign a range of memory address for one core/cluster to its nearest home node, +it can be configured at reset. diff --git a/env/sourceme b/env/sourceme new file mode 100755 index 0000000..1b91c25 --- /dev/null +++ b/env/sourceme @@ -0,0 +1,7 @@ +#!/bin/sh + +CURDIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd ) + +PROJ_ROOT=$CURDIR/.. + +export PROJ_ROOT \ No newline at end of file diff --git a/flow/lint/Makefile b/flow/lint/Makefile new file mode 100755 index 0000000..a7043b0 --- /dev/null +++ b/flow/lint/Makefile @@ -0,0 +1,14 @@ +NAME := rvh_core + +run-goals: run-goal-design_read run-goal-lint_lint_rtl +run-goal-design_read: + sg_shell -tcl spyglass-run-design_read.tcl + +run-goal-lint_lint_rtl: + sg_shell -enable_pass_exit_codes -tcl spyglass-run-lint_lint_rtl.tcl + +run-gui: + spyglass -project $(NAME).prj + +clean: + @rm -rf rvh_core diff --git a/flow/lint/rvh_core.prj b/flow/lint/rvh_core.prj new file mode 100755 index 0000000..490860a --- /dev/null +++ b/flow/lint/rvh_core.prj @@ -0,0 +1,25 @@ +#!SPYGLASS_PROJECT_FILE +#!VERSION 3.0 +# ------------------------------------------------------------------- +# This is a automatically generated project file by fusesoc. +# ------------------------------------------------------------------- + + +set_option projectwdir . +set_option language_mode mixed +set_option designread_enable_synthesis yes +set_option designread_disable_flatten no + +# Make no only FATAL messages return a non-zero exit code, but also ERRORs and +# WARNINGs +set_option enable_pass_exit_codes yes + + +read_file -type sourcelist ../../tb/flist_mesh.syn.f + +set_option top top_mesh_syn +set_option enableSV09 yes +set_option active_methodology $SPYGLASS_HOME/GuideWare/latest/block/rtl_handoff + +current_methodology $SPYGLASS_HOME/GuideWare/latest/block/rtl_handoff + diff --git a/flow/lint/spyglass-run-design_read.tcl b/flow/lint/spyglass-run-design_read.tcl new file mode 100755 index 0000000..0e3cd12 --- /dev/null +++ b/flow/lint/spyglass-run-design_read.tcl @@ -0,0 +1,21 @@ +open_project rvh_core.prj + +current_goal Design_Read + +# Parameters which are not used/defined in a given goal/methodology raise +# a WARNING, which fails the lint process. That's a bit over the top, we hence +# disable this warning. +waive -rule {checkCMD_unknown} + +set rc [run_goal] +close_project -force + +set errorCode [lindex $rc 0] +set errorMsg [lindex $rc 1] +if { $errorCode } { + puts stderr "SpyGlass run failed: $errorMsg ($errorCode)" +} + +# requires sg_shell to be called with -enable_pass_exit_codes, otherwise +# all non-fatal exit codes are mapped to 0 +exit $errorCode diff --git a/flow/lint/spyglass-run-lint_lint_rtl.tcl b/flow/lint/spyglass-run-lint_lint_rtl.tcl new file mode 100755 index 0000000..405dc9a --- /dev/null +++ b/flow/lint/spyglass-run-lint_lint_rtl.tcl @@ -0,0 +1,21 @@ +open_project rvh_core.prj + +current_goal lint/lint_rtl + +# Parameters which are not used/defined in a given goal/methodology raise +# a WARNING, which fails the lint process. That's a bit over the top, we hence +# disable this warning. +waive -rule {checkCMD_unknown} + +set rc [run_goal] +close_project -force + +set errorCode [lindex $rc 0] +set errorMsg [lindex $rc 1] +if { $errorCode } { + puts stderr "SpyGlass run failed: $errorMsg ($errorCode)" +} + +# requires sg_shell to be called with -enable_pass_exit_codes, otherwise +# all non-fatal exit codes are mapped to 0 +exit $errorCode diff --git a/flow/syn/Makefile b/flow/syn/Makefile new file mode 100644 index 0000000..4930e13 --- /dev/null +++ b/flow/syn/Makefile @@ -0,0 +1,33 @@ +## prefered path: proj_root/syn/dc/ + + +export TIMESTAMP=$(shell date +%Y%m%d%H%M%S) +export DESIGN_NAME=rvh_noc +# T12/GF22 +export SYN_PDK=T12 +# SINGLE_ROUTER/MESH +export SYN_TOP=SINGLE_ROUTER + +all: + export TIMESTAMP=$(shell date +%Y%m%d%H%M%S) + export DESIGN_NAME=rvh_noc + export SYN_PDK=T12 + export SYN_TOP=SINGLE_ROUTER + + mkdir ./$(TIMESTAMP)_$(SYN_PDK)_$(SYN_TOP)_run && cd ./$(TIMESTAMP)_$(SYN_PDK)_$(SYN_TOP)_run && mkdir rpt && mkdir output + + /opt/cad/synopsys/installs/syn/Q-2019.12-SP5-1/bin/dc_shell-t -checkout DesignWare -f tcl_scripts/synth.tcl | tee $(TIMESTAMP)_$(SYN_PDK)_$(SYN_TOP)_run/$(SYN_PDK)_$(SYN_TOP).log + +PHONY: +clean: + rm -rf *_run + rm *.log + + +# -topographical_mode means using physical constraints on your design, this will allow you to +# accurately predict post-layout timing, area, and power during synthesis without the need for +# timing approximations based on wire load models. It uses placement and optimization +# technologies to drive accurate timing prediction within synthesis and automatically performs +# leakage power optimization, ensuring better correlation with the final physical design. + + diff --git a/flow/syn/tcl_scripts/assert.tcl b/flow/syn/tcl_scripts/assert.tcl new file mode 100644 index 0000000..92b7c2f --- /dev/null +++ b/flow/syn/tcl_scripts/assert.tcl @@ -0,0 +1,53 @@ +proc assert {cond {msg ""}} { + if {![uplevel 1 expr $cond]} { + return -code error "$msg\n assertion failed: $cond" + } +} + +proc assert_is_file { fpath } { + global ours + if {![file isfile $fpath]} { + error "Error: file ($fpath) not found" +# if {$ours(tcl_check_only) == "false"} { +# uplevel suspend +# } else { +# error "" +# } + } +} + +proc assert_is_dir { dpath } { + global ours + if {![file isdirectory $dpath]} { + error "Error: dir ($dpath) not found" +# if {$ours(tcl_check_only) == "false"} { +# uplevel suspend +# } else { +# error "" +# } + } +} + +proc assert_exist { dpath } { + global ours + if {![file exists $dpath]} { + error "Error: dir ($dpath) not found" +# if {$ours(tcl_check_only) == "false"} { +# uplevel suspend +# } else { +# error "" +# } + } +} + +proc assert_is_file_list { ls_fpath } { + foreach fpath $ls_fpath { + assert_is_file $fpath + } +} + +proc assert_is_dir_list { ls_dpath } { + foreach dpath $ls_dpath { + assert_is_dir $dpath + } +} diff --git a/flow/syn/tcl_scripts/constraints.sdc b/flow/syn/tcl_scripts/constraints.sdc new file mode 100644 index 0000000..d624454 --- /dev/null +++ b/flow/syn/tcl_scripts/constraints.sdc @@ -0,0 +1,315 @@ +#************************************************************** +# Create Clock +#************************************************************** +set clk_period 1 +# set clk_period 0.66 +# set clk_uncertainty [expr $clk_period * 0.1] +set clk_uncertainty_setup [expr $clk_period * 0.2] +set clk_uncertainty_hold [expr $clk_period * 0.05] + +set latency_input_transition [expr $clk_period * 0.04] +set latency_input_transition_clocks [expr $clk_period * 0.003] + +#MARGIN 0.9/0.65/1.0 +set MARGIN 0.9 +set period_1000m [expr $MARGIN * $clk_period]; + +#************************************************************** +# List clk ports +#************************************************************** +set CLK_PORT_LIST_IN "clk" +set RST_PORT_LIST_IN "rst" + +set main_clk [get_ports clk] +#************************************************************** +# Create Clock +#************************************************************** +#create_clock -period 0.500 -waveform {0.0 0.25} -name clk [get_ports clk] +set period_main_clock $period_1000m + +#create_clock -name CLK -period $clk_period [get_ports clk] +create_clock -name clk -period $period_main_clock -waveform [list 0 [expr $period_main_clock *0.5]] $main_clk -add +# create_clock -name vir_main_clk -period $period_main_clock -waveform [list 0 [expr $period_main_clock *0.5]] + +#************************************************************** +# Create Generated Clock +#************************************************************** +#derive_pll_clocks + +#************************************************************** +# ScanEnable to be 0 +#************************************************************** +# Please include vcore_fp_top.disable_scan_constraints.v + +set DRC_data_max_transition 0.25 +set DRC_clock_max_transition 0.15 +#************************************************************** +# Power Pin +#************************************************************** +#set ours(iport,cfg_pwr) [get_ports {*/cfg_is_clk_gated */cfg_thread_cnt*} -filter "direction==in"] + + + + +#************************************************************** +# Set Clock Latency +#************************************************************** + + + +#************************************************************** +# Set Clock Uncertainty +#************************************************************** +#derive_clock_uncertainty +#set_clock_uncertainty 0.125 [all_clocks] +# set_clock_uncertainty $clk_uncertainty [all_clocks] +set_clock_uncertainty -setup $clk_uncertainty_setup [all_clocks] +set_clock_uncertainty -hold $clk_uncertainty_hold [all_clocks] + +#************************************************************** +# Set I/O Delay +#************************************************************** +# set_input_delay [expr 0.5 * $clk_period] -max -clock vir_main_clk [remove_from_collection [all_inputs] [get_ports clk]] +# set_input_delay [expr 0.0 * $clk_period] -min -clock vir_main_clk [remove_from_collection [all_inputs] [get_ports clk]] +# set_output_delay [expr 0.5 * $clk_period] -max -clock vir_main_clk [all_outputs] +# set_output_delay [expr 0.0 * $clk_period] -max -clock vir_main_clk [all_outputs] +set_input_delay [expr $clk_period * 0.4] -clock clk [remove_from_collection [all_inputs] {clk}] +set_output_delay [expr $clk_period * 0.4] -clock clk [all_outputs] + +#************************************************************** +# Set ideal_network +#************************************************************** +if {[sizeof_collection [get_cells -quiet -hierarchical -filter "ref_name =~CKLNQD*"]]} { + set_ideal_network [get_pins -filter "pin_direction == out" -of_object [get_cells -hierarchical -filter "ref_name =~ *CKLNQD*"]] +} +if {[sizeof_collection [get_cells -quiet -hierarchical -filter "ref_name =~CKLHQD*"]]} { + set_ideal_network [get_pins -filter "pin_direction == out" -of_object [get_cells -hierarchical -filter "ref_name =~ *CKKHQD*"]] +} + +set_ideal_network [get_ports -quiet "$CLK_PORT_LIST_IN "] +set_ideal_network [get_ports -quiet "$RST_PORT_LIST_IN "] + +# set_ideal_network [get_attribute [get_clocks -filter "full_name !~ vir*"] source] +# set_dont_touch_network [get_clocks -filter "full_name !~ vir*"] + +set_dont_touch_network [all_clocks] +set_ideal_network [all_fanout -flat -clock_tree] + +#************************************************************** +# Set False Path +#************************************************************** +# false path to power switch control ack (use pre-placed and pre-routing in ICC2) +#set_false_path -from [get_ports */cfg_is_clk_gated] -to [get_ports */cfg_is_clk_gated] +#set_false_path -from [get_ports */cfg_thread_cnt* ] -to [get_ports */cfg_thread_cnt*] + +# false path from SLP to Q in SRAM (SLP not valid in VCORE yet, only on top level) +#set ours(pin,sram_slp) [get_pins -of [get_cells * -hier -filter "ref_name=~TS*N28HPCP*"] -filter "full_name=~*/SLP"] +#set ours(pin,sram_q) [get_pins -of [get_cells * -hier -filter "ref_name=~TS*N28HPCP*"] -filter "full_name=~*/Q*"] +#set_false_path -th $ours(pin,sram_slp) -th $ours(pin,sram_q) + +#************************************************************** +# Begin +#************************************************************** +set global_setup_uncertainty 0.09 +set global_hold_uncertainty 0.08 +set ENV_data_input_max_transition 0.25 +set ENV_input_min_transition 0.010 +set DRC_max_fanout_inner 40 +set DRC_max_fanout_input 1 + +set_max_transition $DRC_data_max_transition [current_design] +set_max_transition $DRC_data_max_transition -data_path [all_clocks] +set_max_transition $DRC_clock_max_transition -clock_path [all_clocks] + +set_max_fanout $DRC_max_fanout_inner [current_design];#40 +set_max_fanout $DRC_max_fanout_input [all_inputs];#1 + +set inputs [remove_from_collection [all_inputs] $CLK_PORT_LIST_IN] +set_input_transition -max $ENV_data_input_max_transition $inputs +set_input_transition -min $ENV_input_min_transition $inputs + +set_load -max 0.050 [all_outputs] +set_load -max 0.004 [all_outputs] + + +if {[info exists dft_clks]} { + set func_clocks [remove_from_collection [all_clocks] $dft_clks] +} else { + set func_clocks [all_clocks] +} + +set_clock_uncertainty -setup $global_setup_uncertainty $func_clocks +set_clock_uncertainty -hold $global_hold_uncertainty [all_clocks] + +#************************************************************** +# Set Multicycle Path +#************************************************************** + +# set_multicycle_path 18 -setup -end \ +# -through [get_pins div_u/ff_div_a_u/q[*]] \ +# -through [get_pins div_u/ff_div_output_data_u/d[*]] +# set_multicycle_path 17 -hold -end \ +# -through [get_pins div_u/ff_div_a_u/q[*]] \ +# -through [get_pins div_u/ff_div_output_data_u/d[*]] +# set_multicycle_path 18 -setup -end \ +# -through [get_pins div_u/ff_div_b_u/q[*]] \ +# -through [get_pins div_u/ff_div_output_data_u/d[*]] +# set_multicycle_path 17 -hold -end \ +# -through [get_pins div_u/ff_div_b_u/q[*]] \ +# -through [get_pins div_u/ff_div_output_data_u/d[*]] + +# set_multicycle_path -setup 2 -through [get_cells {btb_u/BTB_SET_*__btb_tag/*}] -to [get_cells { \ +# npc_gen_u/* \ +# icache_u/req_valid_s1_reg \ +# instr_buffer/wr_ptr_reg* \ +# kill_f1_reg \ +# btq_u/target_ram_reg* \ +# }] + +# set_multicycle_path -setup 2 -through [get_pins {btb_u/BTB_SET_*__btb_tag/*}] + +# set_multicycle_path -setup 8 -through [get_pins {EX/DIV/*}] -to [get_pins { \ +# EX/DIV/DW_DIV_SEQ/* \ +# EX/DIV/* \ +# }] +# set_multicycle_path -setup 8 -through [get_pins {EX/DIV/*}] +# set_multicycle_path -hold 7 -through [get_pins {EX/DIV/*}] +# set_multicycle_path -setup 8 -through [get_pins {EX/DIV/*/*}] +# set_multicycle_path -hold 7 -through [get_pins {EX/DIV/*/*}] +# set_multicycle_path -setup 5 -through [get_pins {EX/MUL/*}] +# set_multicycle_path -hold 4 -through [get_pins {EX/MUL/*}] +# set_multicycle_path -hold 7 -through [get_cells {EX/DIV/*}] -to [get_cells { \ +# EX/DIV/* \ +# EX/DIV/DW_DIV_SEQ_part_rem_reg_reg_*_ \ +# EX/DIV/DW_DIV_SEQ_shf_reg_reg_*__*_ \ +# EX/DIV/DW_DIV_SEQ*reg* \ +# EX/ex2ma_ff_reg** \ +# ID/id2div_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_fp_rs1* \ +# ID/id2fp_add_d_ff_reg* \ +# ID/id2fp_add_s_ff_reg* \ +# ID/id2fp_div_d_ff_reg* \ +# ID/id2fp_div_s_ff_reg* \ +# ID/id2fp_mac_d_ff_reg* \ +# ID/id2fp_mac_s_ff_reg* \ +# ID/id2fp_misc_ff_reg* \ +# ID/id2fp_sqrt_d_ff_reg* \ +# ID/id2fp_sqrt_s_ff_reg* \ +# ID/id2mul_ff_reg* \ +# itlb_u/dff_sfence_req_reg* \ +# }] + +# set_multicycle_path -hold 7 -through [get_pins {EX/DIV/*}] -to [get_pins { \ +# EX/DIV/DW_DIV_SEQ/* \ +# EX/DIV/* \ +# }] + +# set_multicycle_path -setup 5 -through [get_cells {EX/MUL/*}] -to [get_cells { \ +# EX/ex2ma_ff_reg* \ +# EX/MUL/DW_MULT_SEQ*reg* \ +# EX/MUL/* \ +# ID/id2div_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_fp_rs1_ff_reg* \ +# ID/id2fp_add_d_ff_reg* \ +# ID/id2fp_add_s_ff_reg* \ +# ID/id2fp_div_d_ff_reg* \ +# ID/id2fp_div_s_ff_reg* \ +# ID/id2fp_mac_d_ff_reg* \ +# ID/id2fp_mac_s_ff_reg* \ +# ID/id2fp_misc_ff_reg* \ +# ID/id2fp_sqrt_d_ff_reg* \ +# ID/id2fp_sqrt_s_ff_reg* \ +# ID/id2mul_ff_reg* \ +# itlb_u/dff_sfence_req_reg* \ +# }] + +# set_multicycle_path -setup 5 -through [get_pins {EX/MUL/*}] -to [get_pins { \ +# EX/MUL/DW_MULT_SEQ/* \ +# EX/MUL/* \ +# }] +# set_multicycle_path -hold 4 -through [get_cells {EX/MUL/*}] -to [get_cells { \ +# EX/ex2ma_ff_reg* \ +# EX/MUL/DW_MULT_SEQ*reg* \ +# EX/MUL/* \ +# ID/id2div_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_ff_reg* \ +# ID/id2ex_fp_rs1_ff_reg* \ +# ID/id2fp_add_d_ff_reg* \ +# ID/id2fp_add_s_ff_reg* \ +# ID/id2fp_div_d_ff_reg* \ +# ID/id2fp_div_s_ff_reg* \ +# ID/id2fp_mac_d_ff_reg* \ +# ID/id2fp_mac_s_ff_reg* \ +# ID/id2fp_misc_ff_reg* \ +# ID/id2fp_sqrt_d_ff_reg* \ +# ID/id2fp_sqrt_s_ff_reg* \ +# ID/id2mul_ff_reg* \ +# itlb_u/dff_sfence_req_reg* \ +# }] +# set_multicycle_path -hold 4 -through [get_pins {EX/MUL/*}] -to [get_pins { \ +# EX/MUL/DW_MULT_SEQ/* \ +# EX/MUL/* \ +# }] + + +# set ours(ff,fp) [get_cells -hier {id2ex_ctrl_ff_reg*fp_* id2fp_add_s_ff_reg* id2fp_add_d_ff_reg* id2fp_mac_s_ff_reg* id2fp_mac_d_ff_reg* id2fp_div_s_ff_reg* id2fp_div_d_ff_reg* id2fp_sqrt_s_ff_reg* id2fp_sqrt_d_ff_reg* id2fp_misc_ff_reg*}] +# set ours(ff,fpsqrt) [get_cells -hier {id2fp_sqrt_s_ff_reg*}] + +# # multicycle for fp units +# set_multicycle_path -setup 32 -from $ours(ff,fp) +# set_multicycle_path -hold 31 -from $ours(ff,fp) + +# set_multicycle_path -setup 33 -from $ours(ff,fpsqrt) +# set_multicycle_path -hold 32 -from $ours(ff,fpsqrt) + +# set_multicycle_path -setup 10 -from [get_ports {*_pwr_on* *rst_pc*}] +# set_multicycle_path -hold 9 -from [get_ports {*_pwr_on* *rst_pc*}] + +# ## END ORV64 +# ## ORV64 <-> VP +# #set vcore(orv,vc) [get_cells -hier { *VCORE_DEC_U* *orv2vc* *vc2orv*}] +# ### set vcore(orv,vc) [get_pins -hier {*orv2vc* *vc2orv*}] +# ### set_multicycle_path -setup 6 -from $vcore(orv,vc) +# ### set_multicycle_path -hold 4 -from $vcore(orv,vc) +# ## END ORV64 <-> VP + +# # +# #set_max_delay 1.0 -to _vector_core/u_FP__floatpoint/v_*___vector/regs__*__regs/regs_*__r/wq_reg/D +# #set_max_delay 1.0 -from _vector_core/u_FP__floatpoint/clk -to _vector_core/u_FP__floatpoint/v_*___vector/regs__*__regs/regs_*__r/wq_reg/D + + +# #************************************************************** +# # Set Maximum Delay +# #************************************************************** +# # assuming 14% +# set_input_delay [expr $clk_period * 0.5] -clock CLK [all_inputs] +# set_output_delay [expr $clk_period * 0.5] -clock CLK [all_outputs] + + + +#************************************************************** +# Set Minimum Delay +#************************************************************** + + + +#************************************************************** +# Set Input Transition +#************************************************************** +#set_driving_cell -lib_cell BUFFD12BWP35P140HVT [all_inputs] +#set_load [get_attr [get_lib_pin tcbn28hpcplusbwp35p140hvtssg0p81vm40c_ccs/BUFFD12BWP35P140HVT/I] pin_capacitance] [all_outputs] + + +#set_input_transition $latency_input_transition [all_inputs] +#set_input_transition $latency_input_transition_clocks [get_ports clk] + + +#************************************************************** +# Set Load +#************************************************************** +#set_load [expr $clk_period * 0.01] [all_outputs] diff --git a/flow/syn/tcl_scripts/file_to_list.tcl b/flow/syn/tcl_scripts/file_to_list.tcl new file mode 100644 index 0000000..ea9e9e1 --- /dev/null +++ b/flow/syn/tcl_scripts/file_to_list.tcl @@ -0,0 +1,139 @@ +#!/bin/env tclsh + +source [file join [file dirname [file normalize [info script]]] "parse_arg.tcl"] +source [file join [file dirname [file normalize [info script]]] "assert.tcl"] + +proc resolve_env path { + regsub -all {\$\{?([^\s\/\{\}]+)\}?} $path {$::env(\1)} path + eval "set resolved $path" + return $resolved +} + +proc file_to_list args { + parse_arg "-fpath(path)() -col(int)(1) -prefix(string)(.) -suffix(string)(.)" $args + if {![file isfile $fpath]} { + error "Error: file ($fpath) is missing" + } + set ls [list] + set f [open $fpath "r"] + while {[gets $f line] >= 0} { + set line [string trim $line] + if {[string length $line] == 0 || [string index $line 0] == "#" || [string range $line 0 1] == "//"} { + continue + } + regsub -all {\s+} $line " " line + set ls_line [split $line " "] + set item [lindex $ls_line [expr $col-1]] + + lappend ls $item + } + close $f + + if {$prefix != "."} { + set ls_new [list] + foreach item $ls { + lappend ls_new "${prefix}${item}" + } + set ls $ls_new + } + + if {$suffix != "."} { + set ls_new [list] + foreach item $ls { + lappend ls_new "${item}${suffix}" + } + set ls $ls_new + } + + return $ls +} + +proc file_list_remove_duplicate {ls_fpath} { + set ls_new [list] + foreach fpath $ls_fpath { + set fpath_norm [file normalize $fpath] + if {[lsearch -exact $ls_new $fpath_norm] == -1} { + lappend ls_new $fpath_norm + } + } + + return $ls_new +} + +proc expand_file_list {file_list_fpath} { + if {![file isfile $file_list_fpath]} { + error "Error: file ($file_list_fpath) is missing" + } + + set file_list_dpath [file dirname $file_list_fpath] + + set ls_file [list] + + set f [open $file_list_fpath "r"] + while {[gets $f line] >= 0} { + set line [string trim $line] + if {[string length $line] == 0 || [string index $line 0] == "#" || [string range $line 0 1] == "//"} { + continue + } + if {[string match +* $line]} { + continue + } + regsub -all {\s+} $line " " line + set ls_item [split $line " "] + + if {[llength $ls_item] == 1} { + # single item + set fpath [lindex $ls_item 0] + set fpath [resolve_env $fpath] + + if {[string index $fpath 0] == "/"} { + # absolute path + } else { + # relative path + set fpath [file join $file_list_dpath $fpath] + } + assert_exist $fpath + lappend ls_file $fpath + + } elseif {[llength $ls_item] == 2} { + if {[lindex $ls_item 0] == "-f"} { + set fpath [resolve_env [lindex $ls_item 1]] + set nested_file_list_fpath [file join $file_list_dpath $fpath] + set ls_file [concat $ls_file [expand_file_list $nested_file_list_fpath]] + } elseif {[lindex $ls_item 0] == "-v"} { + set fpath [lindex $ls_item 1] + set fpath [resolve_env $fpath] + + if {[string index $fpath 0] == "/"} { + # absolute path + } else { + # relative path + set fpath [file join $file_list_dpath $fpath] + } + + assert_exist $fpath + lappend ls_file $fpath + } elseif {[lindex $ls_item 0] == "-y"} { + set path [resolve_env [lindex $ls_item 1]] + set ls_file [concat $ls_file [glob -nocomplain -directory $path *.sv]] + set ls_file [concat $ls_file [glob -nocomplain -directory $path *.v]] + } else { + error "Error: line ($line) is not recognized" + } + } else { + error "Error: line ($line) is not recognized" + } + } + close $f + + return [file_list_remove_duplicate $ls_file] +} + +if {[info exists argv0]} { + if {[file tail $argv0] == [file tail [info script]]} { + foreach fpath [expand_file_list [lindex $argv 0]] { + puts $fpath + } + } +} + diff --git a/flow/syn/tcl_scripts/parse_arg.tcl b/flow/syn/tcl_scripts/parse_arg.tcl new file mode 100644 index 0000000..2894dbe --- /dev/null +++ b/flow/syn/tcl_scripts/parse_arg.tcl @@ -0,0 +1,130 @@ +#!/bin/env tclsh +################################################################ +# prase_arg +# ls_arg_definition +# one expected argument = "-${arg_name}(type)(default_value)" +# arg_name will be defined in up-level proc. +# To match defined arguments, we support shortcuts like Synopsys DC/PT/ICC +# type = +# default is the default value if it's not given in ls_arg. +# if default is empty, then this is a must-have argument. +# ls_arg can be $args for a proc or $argv for command line script +################################################################ + +proc parse_arg { ls_arg_definition ls_arg } { + # parse ls_arg_definition + set arg(name) {} + set usage "usage:\n" + foreach arg_def $ls_arg_definition { + if {[regexp {^-([_0-9a-zA-Z]+)\(([a-z]+)\)\((.*)\)$} $arg_def match arg_name arg_type default_value]} { + if {$default_value == ""} { + set default_value "NULL" + set optional "Must-have" + } else { + set optional "Optional" + } + set arg(name) [lappend arg(name) $arg_name] + set arg($arg_name,arg_type) $arg_type + set arg($arg_name,value) $default_value + uplevel set $arg_name $arg($arg_name,value) + # usage + set usage "$usage -$arg_name ($optional) TYPE=$arg_type; DEFAULT=$default_value\n" + } else { + error "Error: arg_definition ($arg_def) is not recognized" + } + } + + # parse ls_arg + if {$ls_arg == "-help"} { + puts $usage + uplevel exit + } + if {$ls_arg == ""} { + set expect_arg_name -1 + } else { + set expect_arg_name 1 + } + while {$expect_arg_name >= 0} { + set input [lindex $ls_arg 0] + set ls_arg [lrange $ls_arg 1 end] + + if {$expect_arg_name} { + # parse arg name + if {[regexp {^-([_0-9a-zA-Z]+)$} $input match input_arg_name]} { + set num_match 0 + set match_arg_name "" + foreach arg_name $arg(name) { + if {[string match "${input_arg_name}*" $arg_name]} { + incr num_match 1 + set match_arg_name $arg_name + } + } + if {$num_match == 0} { + # no match found + error "Error: argument ($input_arg_name) is not defined in ($ls_arg_definition)." + } elseif {$num_match > 1} { + # multi matches found + error "Error: argument ($input_arg_name) is too ambiguouse. $num_match matches are found." + } else { + # 1 match found + if {$arg($match_arg_name,arg_type) == "bool"} { + set arg($match_arg_name,value) 1 + set expect_arg_name 1 + } else { + set arg($match_arg_name,value) "NULL" + set expect_arg_name 0 + } + } + } else { + error "Error: expecting argument name at ($input)" + } + } else { + # parse arg value + if {($arg($match_arg_name,arg_type) == "int" && ![regexp {^[0-9]+$} $input]) \ + || ($arg($match_arg_name,arg_type) == "float" && ![regexp {^[0-9]+\.[0-9]+} $input]) \ + } { + error "Error: argument type is wrong. Expecting $arg($match_arg_name,arg_type) for $match_arg_name at ($input)" + } elseif {$arg($match_arg_name,arg_type) == "string"} { + regsub -all {\$} $input {\\$} input + regsub -all "\n" $input "\\n" input + regsub -all "\t" $input "\\t" input + regsub -all "\"" $input "\\\"" input + set arg($match_arg_name,value) "\"$input\"" + } else { + set arg($match_arg_name,value) $input + } + set expect_arg_name 1 + } + + if {[llength $ls_arg] == 0} { + break + } +} + +foreach arg_name $arg(name) { + if {$arg($arg_name,value) == "NULL"} { + error "Error: argument ($arg_name) has no value defined" + } + uplevel set $arg_name $arg($arg_name,value) +} +} + +# test +if {[info exists argv0]} { + if {[file tail $argv0] == [file tail [info script]]} { + + proc test_parse_arg args { + parse_arg "-num(int)(100) -input_file(path)() -output_file(path)() -is_cool(bool)(0) -some_string(string)(.)" $args + puts $num + puts $input_file + puts $output_file + puts $is_cool + puts "($some_string)" + } + + test_parse_arg -num 123 -in ./input -out ../output -is_cool + test_parse_arg -num 123 -in ./input -out ../output + + } +} + diff --git a/flow/syn/tcl_scripts/synth.tcl b/flow/syn/tcl_scripts/synth.tcl new file mode 100644 index 0000000..37d1f9d --- /dev/null +++ b/flow/syn/tcl_scripts/synth.tcl @@ -0,0 +1,257 @@ +# choose pdk +# set SYN_PDK GF22 +# set SYN_PDK T12 + +# choose synthesis top +# set SYN_TOP SINGLE_ROUTER +# set SYN_TOP MESH + + +# start +echo "RUN STARTED AT [date]" + + +if { $env(SYN_PDK)=="GF22" } { + # gf22n + source tcl_scripts/synth_init_lib.tcl +} elseif { $env(SYN_PDK)=="T12" } { + # t12n + source tcl_scripts/synth_init_library.t12.tcl +} else { + exit 1 +} + +#start from the path of Makefile rather than# + +# set saving path of formality file +set DESIGN_NAME $env(DESIGN_NAME) + +if { $env(SYN_TOP)=="MESH" } { + # mesh + set TOP_NAME top_mesh_syn + set FLIST_NAME flist_mesh.syn.f +} else { + # single_router + set TOP_NAME top_single_router_syn + set FLIST_NAME flist_single_router.syn.f +} + + +set_svf ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}.synth.svf + +# setup will be included in .synopsys_dc.setup file +source ./.synopsys_dc.setup + + +################################################################### + +#------------------------Specify the libraries---------------------# +set_app_var search_path "$search_path ." +#license is needed# + +if { $env(SYN_PDK)=="GF22" } { + # gf22n + set_app_var target_library [concat "$DB(ssg0p45v,m40c)"] +} elseif { $env(SYN_PDK)=="T12" } { + # t12n + set_app_var target_library [concat "$DB(ssg0p72v,125c)"] +} else { + exit 1 +} + +#.db, TODO: simplify the way of importing target libs# +#----designware setting-------# +set_app_var synthetic_library "dw_foundation.sldb" +set_dp_smartgen_options -hierarchy -smart_compare true -tp_oper_sel auto -tp_opt_tree auto -brent_kung_adder true -adder_radix auto -inv_out_adder_cell auto -mult_radix4 auto -sop2pos_transformation auto -mult_arch auto -optimize_for area,speed +#Analyzes DesignWare datapath extraction.# + +set_app_var link_library "* $target_library $synthetic_library" +#all libs which might be used# + +#------------------------- Read the design ------------------------# +#----------------------# +## read +### in dc_shell: read -format sverilog rtl.sv +### in tcl shell: read_verilog rtl.v; read_db lib.db + +#---------------------# +## or analyze+elaborate+WORK dir(default) + +define_design_lib WORK -path ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/WORK +#can be omitted# +source tcl_scripts/file_to_list.tcl + +analyze -format sverilog [concat [expand_file_list "$env(PROJ_ROOT)/tb/${FLIST_NAME}"]] +#analyze HDL source code and save intermediate results named .syn in ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/work dir, which can be used by elaborate directly even without anlyzing; TODO: what does es1y_define.sv mean?# +elaborate ${TOP_NAME} +# write_file -hierarchy -format verilog -output output/rvh1.synth.elaborate.v +#for dbg# +current_design ${TOP_NAME} +link +#not necessary after anal. and elab.?, link lib has been defined before# + +analyze_datapath_extraction -no_autoungroup + + +#-------------------- Define the design environment -------------------# +# set_load 2.2 sout +# set_load 1.5 cout +# set_driving_cell -lib_cell FD1 [all_inputs] + +#---------------------- Set the design constraints --------------------# + +## Design Rule constraints +# set_max_transistion +# set_max_fanout +# set_max_capacitance +#provided by foundary company, can be setted tightly in advance; TODO: get precise indicators# + +## Set the optimization constraints + +#----delay----# + +#----area-----# + +set_host_options -max_cores 16 +source tcl_scripts/constraints.sdc + +set_clock_transition 0.1 [all_clocks] + +set_critical_range 10 [current_design] + +group_path -weight 0.1 -name input_path -from [all_inputs] +group_path -weight 0.1 -name output_path -to [all_outputs] +group_path -weight 0.1 -name in2out -from [all_inputs] -to [all_outputs] + +set_dynamic_optimization false +set_leakage_optimization false + +set compile_timing_high_effort true +set placer_tns_driven true +set psynopt_tns_high_effort true +set compile_timing_high_effort_tns true +set_cost_priority -delay + +puts "TIMESTAMP Pre-Compile [clock format [clock second ] -format %T] [expr [mem] /1024]M" + +set compile_final_drc_fix all +set compile_automatic_clock_phase_inference relaxed +set compile_enable_constant_propagation_with_no_boundary_opt true + +set compile_advanced_fix_multiple_port_nets true +set compile_rewire_multiple_port_nets true +set_fix_multiple_port_nets -all -buffer_constants [get_designs *] +set_auto_disable_drc_nets -clock true -constant true -on_clock_network true + +#copied from original /script/vp_fp.03-05-2020_20.30.59.sdc, TODO: define accurate constraints and optimizations# + +# write_sdc output/rvh1.synth.elaborate.sdc +# report_clock_tree -structure > rpt/clock_tree_structure.rpt +#for dbg# + +#--------------------- Define clock gating -------------------------# +set_clock_gating_style -sequential_cell latch -positive_edge_logic {integrated} -negative_edge_logic {integrated} \ + -control_point before -control_signal scan_enable \ + -minimum_bitwidth 4 -observation_point false \ + -max_fanout 16 + +set_clock_latency 0 [all_clocks] +set_clock_gate_latency -overwrite -stage 0 -fanout_latency {1-inf 0} +set_clock_gate_latency -overwrite -stage 1 -fanout_latency {1-inf -0.05} +set_clock_gate_latency -overwrite -stage 2 -fanout_latency {1-inf -0.1} +set_clock_gate_latency -overwrite -stage 3 -fanout_latency {1-inf -0.15} +set_clock_gate_latency -overwrite -stage 4 -fanout_latency {1-inf -0.2} +set_clock_gate_latency -overwrite -stage 5 -fanout_latency {1-inf -0.25} + +set ALL_INPUTS [all_inputs] +foreach_in_collection INPUTS $ALL_INPUTS { + append_to_collection -unique INPUT_REG [ filter_collection [all_fanout -from $INPUTS -flat -endpoints_only] "full_name =~ */synch_toggle || full_name =~ */synch_preset || full_name =~ */synch_enable || full_name =~ */synch_clear || full_name =~ */next_state "] +} +set_clock_gating_objects -exclude [get_cells -of_object $INPUT_REG] + + + +#--------------------- Select compile strategy -------------------------# + +#--------------------- Synthesize and optimize the design ------------------------# +# echo [get_object_name [get_lib_cells */* -filter dont_use==true]] > rpt/dont_use_list.rpt +# check_design > rpt/check_design.precompile.rpt +#for dbg# +set_verification_top + +#set_dynamic_optimization true + +#run 2 times compile_ultra +compile_ultra -gate_clock -retime -no_autoungroup -no_boundary_optimization +compile_ultra -gate_clock -retime -no_autoungroup -no_boundary_optimization +#compile_ultra (of DC Ultra) provides concurrent optimization of timing, area, power, and test for high performance designs# +#it also provides advanced delay and arithmetic optimization, advanced timing analysis, automatic leakage power optimization, and register retiming# + +#--------------------- Analyze and debug the design/resolve design problems --------------------# + +analyze_datapath > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/datapath.compile.rpt +report_resources > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/resources.compile.rpt +write_file -hierarchy -format verilog -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/rvh1.synth.compile.v +write_sdc ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/rvh1.synth.compile.sdc + +update_timing +report_timing -nosplit > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/timing.compile.rpt +report_area -nosplit -hier > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/area.hier.compile.rpt + +check_design > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/check_design.preopt.rpt +optimize_netlist -area -no_boundary_optimization +check_design > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/check_design.postopt.rpt + +define_name_rules preserve_struct_bus_rule -preserve_struct_ports +define_name_rules ours_verilog_name_rule -allowed "a-z A-Z 0-9 _" \ + -check_internal_net_name \ + -case_insensitive + +change_names -rules preserve_struct_bus_rule -hierarchy -log_changes ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/struct_name_change.log +change_names -rules ours_verilog_name_rule -hierarchy -log_changes ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/legalize_name_change.log +write -format verilog -hierarchy -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/rvh1.synth.final.v +write -format ddc -hierarchy -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/rvh1.synth.final.ddc +write_sdc -nosplit ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/rvh1.synth.final.sdc + +report_clock_gating > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/clock_gating.rpt +report_timing -tran -net -input -max_paths 500 -significant_digits 3 -nosplit > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.timing.rpt +report_timing -delay_type min -max_paths 500 -input_pins -nets -transition_time -capacitance -significant_digits 3 > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.min_delay.rpt +report_timing -delay_type max -max_paths 500 -input_pins -nets -transition_time -capacitance -significant_digits 3 > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.max_delay.rpt +report_constraint -all_violators -significant_digits 3 > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.all_viol_constraints.rpt +report_area -nosplit -hier > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.area.hier.rpt +report_resources -nosplit -hier > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.resources.rpt +report_timing_requirements > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.mulcycle.rpt +report_compile_options -nosplit > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/synth.compile_options.rpt + +#report_timing -tran -net -input -max_paths 1000 > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/timing.rpt +report_clock_gating -nosplit -verbose -multi_stage > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/clock_gating.rpt +report_clock_gating -gated -nosplit -verbose > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/clock_gating_gated.rpt +report_clock_gating -ungated -nosplit -verbose > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/clock_ungating_gated.rpt +report_power -nosplit > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/power.rpt +report_qor > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/qor.rpt +#report_area -hierarchy > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/area.rpt +#report_constraint -all_violators -verbose -max_capacitance -max_transition > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/drc.rpt +report_clocks > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/clock.rpt +check_design -unmapped > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/check_design.rpt +#check_timing -include {clock_no_period data_check_no_clock generated_clock generic} ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/check_timing.rpt +report_dont_touch -nosplit -class cell > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/dont_touch.rpt +report_threshold_voltage_group > ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/rpt/threshold_voltage_group.rpt + +#--------------------- Save the design database ---------------------# +write_file -format ddc -hierarchy -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}.ddc +#.ddc is the whole project, can be modified and checked# +write_file -format verilog -hierarchy -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}_netlist.v +#netlist.v for P&R and sim# +#write_sdf ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}_sdf +#recording the latency of std cells, also useful for post-sim# +#write_parasitics -output ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}_parasitics +#Writes parasitics in SPEF format or as a Tcl script that contains set_load and set_resistance commands.# +# write_sdc sdc_file_name +#Writes out a script in Synopsys Design Constraints (SDC) format.# +#This script contains commands that can be used with PrimeTime or with Design Compiler. SDC is also licensed by external vendors through the Tap-in program. SDC-formatted script files are read into PrimeTime or Design Compiler using the read_sdc command.# +# write_floorplan -all ./$env(TIMESTAMP)_$env(SYN_PDK)_$env(SYN_TOP)_run/output/${DESIGN_NAME}_phys_cstr_file_name.tcl +#writes a Tcl script file that contains floorplan information for the current or user-specified design. writes commands relative to the top of the design, regardless of the current instance.# + + +#-------------------------------------------------------------------# +echo "RUN ENDED AT [date]" diff --git a/flow/syn/tcl_scripts/synth_init_lib.tcl b/flow/syn/tcl_scripts/synth_init_lib.tcl new file mode 100644 index 0000000..9556443 --- /dev/null +++ b/flow/syn/tcl_scripts/synth_init_lib.tcl @@ -0,0 +1,4 @@ +set DB(ssg0p45v,m40c) { + /work/tech/GF22FDX/LIB/DesignWare_logic_libs/globalfoundaries22nhsda/36hd/edl/ulvt/4.00a/liberty/ccs/gf22nsdvlogl36edl116a_TT_0P80V_0P00V_0P00V_0P00V_25C.db \ + /work/tech/GF22FDX/LIB/DesignWare_logic_libs/globalfoundaries22nhsda/24hd/edl/lvt/4.00a/liberty/ccs/gf22nsdllogl24edl116a_TT_0P80V_0P00V_0P00V_0P00V_25C.db \ + } \ No newline at end of file diff --git a/flow/syn/tcl_scripts/synth_init_library.t12.tcl b/flow/syn/tcl_scripts/synth_init_library.t12.tcl new file mode 100755 index 0000000..4fb6133 --- /dev/null +++ b/flow/syn/tcl_scripts/synth_init_library.t12.tcl @@ -0,0 +1,57 @@ +set DB(ssg0p72v,125c) { +/work/tech/t12nm/Mem/db/ts1n12ffcllsblvtd128x32m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllsblvtd640x34m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta1024x128m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta1024x64m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta1024x72m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta1024x76m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta1024x80m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x256m2swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x262m2swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x32m2swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x51m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x59m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x82m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta128x88m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta16384x30m16swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta16384x32m16swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta16384x39m16swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta16x88m2swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta2048x128m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta2048x66m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta2048x98m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta256x80m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta32x86m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta4096x32m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta4096x32m8swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta4096x64m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta512x78m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta64x84m4swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts1n12ffcllulvta8192x32m8swbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts3n12ffcllulvta1024x64m16bo_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvta1088x34m8fwbsho_150a_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvta288x68m2fwbsho_150a_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvta368x34m2fwbsho_150a_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvta544x67m4fwbsho_150a_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvta88x136m2fwbsho_150a_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb1024x32m4wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb148x136m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x112m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x120m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x128m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x152m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x154m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb16x244m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb20x64m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb256x10m2wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb288x68m2wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb32x138m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb40x64m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb560x33m4wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb64x256m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb64x64m2wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb8x256m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/Mem/db/ts6n12ffcllulvtb8x64m1wbsho_130b_ssgnp0p72v125c.db \ +/work/tech/t12nm/STD/db/tcbn12ffcllbwp20p90cpdlvtssgnp0p72v125c.db +} + diff --git a/rtl/include/rvh_noc_pkg.sv b/rtl/include/rvh_noc_pkg.sv new file mode 100755 index 0000000..dd44ad7 --- /dev/null +++ b/rtl/include/rvh_noc_pkg.sv @@ -0,0 +1,157 @@ +`ifndef __RVH_NOC_PKG_SV__ +`define __RVH_NOC_PKG_SV__ + +// ---------- +// local port configuration +// ---------- +`define HAVE_LOCAL_PORT +`define LOCAL_PORT_NUM_2 // local port num >= 2 +// `define LOCAL_PORT_NUM_3 // local port num >= 3 +// `define LOCAL_PORT_NUM_4 // local port num >= 4 + +`ifdef LOCAL_PORT_NUM_4 + `ifndef LOCAL_PORT_NUM_3 + `define LOCAL_PORT_NUM_3 + `endif + `ifndef LOCAL_PORT_NUM_2 + `define LOCAL_PORT_NUM_2 + `endif +`endif + +`ifdef LOCAL_PORT_NUM_3 + `ifndef LOCAL_PORT_NUM_2 + `define LOCAL_PORT_NUM_2 + `endif +`endif + +// ---------- +// use unified dual-port ram per input port vc data buffer (default: dff) +// ---------- +// `define VC_DATA_USE_DUAL_PORT_RAM + +// ---------- +// reture credit to send at sa stage rather than st atage +// ---------- +// `define RETURN_CREDIT_AT_SA_STAGE + +// ---------- +// whether allow local ports in same router transfer flit, at least 2 local ports +// ---------- +`define ALLOW_SAME_ROUTER_L2L_TRANSFER + +// ---------- +// QoS, at most one of follow macros can be defined +// ---------- + +// `define COMMON_QOS // No special vc, all vc head flits ranked by QoS value. +`define COMMON_QOS_EXTRA_RT_VC // Add special vc for highest priority flits, all vc head flits ranked by QoS value + +// not implemented: +// `define RT_BYPASS_QOS_EXTRA_RT_VC // Add special vc for highest priority flits, other vc head flits have no QoS support + + +`ifdef COMMON_QOS + `define USE_QOS_VALUE +`endif + +`ifdef COMMON_QOS_EXTRA_RT_VC + `define USE_QOS_VALUE +`endif + + +package rvh_noc_pkg; + +localparam CHANNEL_NUM = 4; // 4 channels: req, resp, data, snp + +// 4*4 nodes max +localparam NodeID_X_Width = 2; +localparam NodeID_Y_Width = 2; +localparam NodeID_Device_Port_Width = 2; +localparam NodeID_Device_Id_Width = 1; + +localparam NodeID_Width = NodeID_X_Width + NodeID_Y_Width + NodeID_Device_Port_Width + NodeID_Device_Id_Width; // 7 +localparam TxnID_Width = 12; +localparam QoS_Value_Width = 4; + +localparam FLIT_LENGTH = 256; + +localparam INPUT_PORT_NUMBER = 6; // N,S,E,W,L +localparam INPUT_PORT_NUMBER_IDX_W = INPUT_PORT_NUMBER > 1 ? $clog2(INPUT_PORT_NUMBER) : 1; +localparam OUTPUT_PORT_NUMBER = 6; // N,S,E,W,L +localparam ROUTER_PORT_NUMBER = 4; +localparam LOCAL_PORT_NUMBER = INPUT_PORT_NUMBER-ROUTER_PORT_NUMBER; + +`ifdef COMMON_QOS_EXTRA_RT_VC +localparam QOS_VC_NUM_PER_INPUT = 1; +`else +localparam QOS_VC_NUM_PER_INPUT = 0; +`endif + +localparam VC_ID_NUM_MAX = (CHANNEL_NUM-1)+LOCAL_PORT_NUMBER+QOS_VC_NUM_PER_INPUT; +localparam VC_ID_NUM_MAX_W = VC_ID_NUM_MAX > 1 ? $clog2(VC_ID_NUM_MAX) : 1; + +localparam SA_GLOBAL_INPUT_NUM_MAX = (CHANNEL_NUM-1)+LOCAL_PORT_NUMBER; +localparam SA_GLOBAL_INPUT_NUM_MAX_W = SA_GLOBAL_INPUT_NUM_MAX > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_MAX) : 1; + +localparam VC_DEPTH_MAX = 4; +`ifdef VC_DATA_USE_DUAL_PORT_RAM + `ifdef RETURN_CREDIT_AT_SA_STAGE +localparam VC_DPRAM_DEPTH_MAX = VC_ID_NUM_MAX * (VC_DEPTH_MAX+1); +localparam VC_BUFFER_DEPTH_MAX_W = (VC_DEPTH_MAX+1) > 1 ? $clog2(VC_DEPTH_MAX+1) : 1; + `else +localparam VC_DPRAM_DEPTH_MAX = VC_ID_NUM_MAX * VC_DEPTH_MAX; +localparam VC_BUFFER_DEPTH_MAX_W = VC_DEPTH_MAX > 1 ? $clog2(VC_DEPTH_MAX) : 1; + `endif +localparam VC_DPRAM_DEPTH_MAX_W = VC_DPRAM_DEPTH_MAX > 1 ? $clog2(VC_DPRAM_DEPTH_MAX) : 1; +`endif + +typedef enum logic [2:0] { + N = 0, + S = 1, + E = 2, + W = 3, + L0 = 4, + L1 = 5, + L2 = 6, + L3 = 7 +} io_port_t; + +typedef struct packed { + logic [NodeID_X_Width-1:0] x_position; + logic [NodeID_Y_Width-1:0] y_position; + logic [NodeID_Device_Port_Width-1:0] device_port; + logic [1-1:0] device_id; +} node_id_t; + +`ifdef VC_DATA_USE_DUAL_PORT_RAM +typedef struct packed { + logic [VC_DPRAM_DEPTH_MAX_W-1:0] dpram_idx; // == VC_BUFFER_DEPTH * vc_id + per_vc_idx + logic [VC_BUFFER_DEPTH_MAX_W-1:0] per_vc_idx; +} dpram_used_idx_t; +`endif + +typedef struct packed { + node_id_t tgt_id; // target id + node_id_t src_id; // source id + logic [TxnID_Width-1:0] txn_id; // transaction id + io_port_t look_ahead_routing; +`ifdef USE_QOS_VALUE + logic [QoS_Value_Width-1:0] qos_value; +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + dpram_used_idx_t dpram_used_idx; +`endif +} flit_dec_t; + +typedef struct packed { + logic common_vld; + logic rt_vld; +} vc_select_vld_t; + +typedef struct packed { + logic [VC_ID_NUM_MAX_W-1:0] common_vc_id; + logic [VC_ID_NUM_MAX_W-1:0] rt_vc_id; +} vc_select_vc_id_t; + +endpackage +`endif diff --git a/rtl/input_port.sv b/rtl/input_port.sv new file mode 100644 index 0000000..0fa7947 --- /dev/null +++ b/rtl/input_port.sv @@ -0,0 +1,138 @@ +module input_port +import rvh_noc_pkg::*; +#( + parameter type flit_payload_t = logic[256-1:0], + parameter VC_NUM = 1, + parameter VC_DEPTH = 1, + parameter VC_NUM_IDX_W =VC_NUM > 1 ? $clog2(VC_NUM) : 1, + + parameter INPUT_PORT_NO = 0 +) +( + // input from other router or local port + input logic rx_flit_pend_i, + input logic rx_flit_v_i, + input flit_payload_t rx_flit_i, + input logic [VC_NUM_IDX_W-1:0] rx_flit_vc_id_i, + input io_port_t rx_flit_look_ahead_routing_i, + + // free vc credit sent to sender + output logic rx_lcrd_v_o, + output logic [VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o, + + // output head flit ctrl info to SA & RC unit + output logic [VC_NUM-1:0] vc_ctrl_head_vld_o, + output flit_dec_t [VC_NUM-1:0] vc_ctrl_head_o, + + // output data to switch traversal + output flit_payload_t [VC_NUM-1:0] vc_data_head_o, + + // input pop flit ctrl fifo (comes from SA stage) + input logic inport_read_enable_sa_stage_i, + input logic [VC_NUM_IDX_W-1:0] inport_read_vc_id_sa_stage_i, +`ifdef VC_DATA_USE_DUAL_PORT_RAM + input dpram_used_idx_t inport_read_dpram_idx_i, +`endif + + // input pop flit ctrl fifo (comes from ST stage) + input logic inport_read_enable_st_stage_i, + input logic [VC_NUM_IDX_W-1:0] inport_read_vc_id_st_stage_i, + +`ifndef SYNTHESIS + // router addr + input logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i, + input logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i, +`endif + + input logic clk, + input logic rstn +); + +flit_dec_t flit_ctrl_info; + + +// 1 decode flit, get input vc and routing info +input_port_flit_decoder +#( + .flit_payload_t (flit_payload_t) + // .VC_NUM_IDX_W (VC_NUM_IDX_W) +) +input_port_flit_decoder_u +( + .flit_v_i (rx_flit_v_i ), + .flit_i (rx_flit_i ), + .flit_look_ahead_routing_i(rx_flit_look_ahead_routing_i), + + .flit_dec_o (flit_ctrl_info ) +); + +// 2 input vc fifo +input_port_vc +#( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM ), + .VC_DEPTH (VC_DEPTH ) +) +input_port_vc_u +( + // input from input port + .flit_v_i (rx_flit_v_i ), + .flit_i (rx_flit_i ), + .flit_dec_i (flit_ctrl_info ), + .flit_vc_id_i (rx_flit_vc_id_i), + + // free vc credit sent to sender + .lcrd_v_o (rx_lcrd_v_o ), + .lcrd_id_o (rx_lcrd_id_o ), + + // output ctrl to local allocate + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_o), + .vc_ctrl_head_o (vc_ctrl_head_o ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_o ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage_i ), + .inport_read_vc_id_sa_stage_i (inport_read_vc_id_sa_stage_i ), +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (inport_read_dpram_idx_i ), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage_i ), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage_i ), + + + .clk (clk ), + .rstn (rstn ) +); + + +`ifndef SYNTHESIS +`ifdef V_INPORT_PRINT_EN +// debug print +always_ff @(posedge clk) begin + if(rstn) begin + if(rx_flit_v_i) begin + $display("[%16d] info: receive flit: router:(%d,%d); inport: %1d(N0,S1,E2,W3,L4-7); vc_id: %1d; look_ahead_routing: %1d(N0,S1,E2,W3,L4-7), QoS = %d", + $time(), + node_id_x_ths_hop_i, node_id_y_ths_hop_i, + INPUT_PORT_NO, + rx_flit_vc_id_i, + flit_ctrl_info.look_ahead_routing, + rx_flit_i[QoS_Value_Width-1:0]); + $write(" "); + $display("txn_id: 0x%h, sender: (%d,%d), sender_local_port: %1d", + flit_ctrl_info.txn_id, + flit_ctrl_info.src_id.x_position, flit_ctrl_info.src_id.y_position, + flit_ctrl_info.src_id.device_port); + $write(" "); + $display("tgt_id: (%d,%d), tgt_local_port: %1d", + flit_ctrl_info.tgt_id.x_position, flit_ctrl_info.tgt_id.y_position, flit_ctrl_info.tgt_id.device_port); + end + end +end +`endif +`endif +endmodule diff --git a/rtl/input_port_flit_decoder.md b/rtl/input_port_flit_decoder.md new file mode 100644 index 0000000..1806632 --- /dev/null +++ b/rtl/input_port_flit_decoder.md @@ -0,0 +1,8 @@ +for chi, the least significant bit represents: + +| field | width | bits in flit | comments | +| ----- | --------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| QoS | 4 | [0:3] | ascending values of QoS indicate higher priority levels, not used, place holder | +| TgtID | NodeID_Width, 7 to 11 | [4:4+NodeID_Width-1] | node ID of the component to which the message is targeted | +| SrcID | NodeID_Width, 7 to 11 | [4+NodeID_Width:4+NodeID_Width+NodeID_Width-1] | node ID of the component from which the message is sent | +| TxnID | 12 | [4+NodeID_Width+NodeID_Width:4+NodeID_Width+NodeID_Width+12-1] | transaction identifier of the message | \ No newline at end of file diff --git a/rtl/input_port_flit_decoder.sv b/rtl/input_port_flit_decoder.sv new file mode 100644 index 0000000..74eafcf --- /dev/null +++ b/rtl/input_port_flit_decoder.sv @@ -0,0 +1,24 @@ +module input_port_flit_decoder + import rvh_noc_pkg::*; +#( + parameter type flit_payload_t = logic[256-1:0] + // parameter VC_NUM_IDX_W = 1 +) +( + input logic flit_v_i, + input flit_payload_t flit_i, + input io_port_t flit_look_ahead_routing_i, + + output flit_dec_t flit_dec_o +); + +`ifdef USE_QOS_VALUE +assign flit_dec_o.qos_value = flit_i[QoS_Value_Width-1:0]; +`endif +assign flit_dec_o.tgt_id = flit_i[QoS_Value_Width+NodeID_Width-1:QoS_Value_Width]; +assign flit_dec_o.src_id = flit_i[QoS_Value_Width+NodeID_Width+NodeID_Width-1:QoS_Value_Width+NodeID_Width]; +assign flit_dec_o.txn_id = flit_i[QoS_Value_Width+NodeID_Width+NodeID_Width+TxnID_Width-1:QoS_Value_Width+NodeID_Width+NodeID_Width]; + +assign flit_dec_o.look_ahead_routing = flit_look_ahead_routing_i; + +endmodule \ No newline at end of file diff --git a/rtl/input_port_vc.sv b/rtl/input_port_vc.sv new file mode 100644 index 0000000..4129d7f --- /dev/null +++ b/rtl/input_port_vc.sv @@ -0,0 +1,289 @@ +module input_port_vc + import rvh_noc_pkg::*; +#( + parameter type flit_payload_t = logic[256-1:0], + parameter VC_NUM = 1, + parameter VC_NUM_IDX_W = VC_NUM > 1 ? $clog2(VC_NUM) : 1, + parameter VC_DEPTH = 1, +`ifdef RETURN_CREDIT_AT_SA_STAGE + parameter VC_BUFFER_DEPTH = VC_DEPTH + 1, // need one more slot to handle push and pop at the same cycle when the fifo is full +`else + parameter VC_BUFFER_DEPTH = VC_DEPTH, +`endif + parameter VC_BUFFER_DEPTH_IDX_W = VC_BUFFER_DEPTH > 1 ? $clog2(VC_BUFFER_DEPTH) : 1 + +`ifdef VC_DATA_USE_DUAL_PORT_RAM + , + parameter VC_DPRAM_DEPTH = VC_NUM * VC_BUFFER_DEPTH, + parameter VC_DPRAM_DEPTH_IDX_W = VC_DPRAM_DEPTH > 1 ? $clog2(VC_DPRAM_DEPTH) : 1 +`endif +) +( + // input from input port + input logic flit_v_i, + input flit_payload_t flit_i, + input flit_dec_t flit_dec_i, + input logic [VC_NUM_IDX_W-1:0] flit_vc_id_i, + + // free vc credit sent to sender + output logic lcrd_v_o, + output logic [VC_ID_NUM_MAX_W-1:0] lcrd_id_o, + + // output ctrl to local allocate + output logic [VC_NUM-1:0] vc_ctrl_head_vld_o, + output flit_dec_t [VC_NUM-1:0] vc_ctrl_head_o, + + // output data to switch traversal + output flit_payload_t [VC_NUM-1:0] vc_data_head_o, + + // input pop flit ctrl fifo (comes from SA stage) + input logic inport_read_enable_sa_stage_i, + input logic [VC_NUM_IDX_W-1:0] inport_read_vc_id_sa_stage_i, // use local sa result instead of vc assignment +`ifdef VC_DATA_USE_DUAL_PORT_RAM + input dpram_used_idx_t inport_read_dpram_idx_i, +`endif + + // input pop flit ctrl fifo (comes from ST stage) + input logic inport_read_enable_st_stage_i, + input logic [VC_NUM_IDX_W-1:0] inport_read_vc_id_st_stage_i, + + input logic clk, + input logic rstn +); + +genvar i; + +logic [VC_NUM-1:0] vc_data_tail_we; +flit_payload_t vc_data_din; +logic [VC_NUM-1:0] vc_data_enqueue_rdy; + +logic [VC_NUM-1:0] vc_ctrl_tail_we; +flit_dec_t vc_ctrl_din; +logic [VC_NUM-1:0] vc_ctrl_enqueue_rdy; + +logic [VC_NUM-1:0] vc_data_head_vld; +flit_payload_t [VC_NUM-1:0] vc_data_head; +logic [VC_NUM-1:0] vc_data_head_dequeue_vld; + +logic [VC_NUM-1:0] vc_ctrl_head_vld; +flit_dec_t [VC_NUM-1:0] vc_ctrl_head; +logic [VC_NUM-1:0] vc_ctrl_head_dequeue_vld; + +`ifdef VC_DATA_USE_DUAL_PORT_RAM + logic [VC_NUM-1:0][VC_BUFFER_DEPTH_IDX_W-1:0] vc_data_freelist_deq_per_vc_idx; + logic [VC_BUFFER_DEPTH_IDX_W-1:0] vc_data_freelist_deq_per_vc_idx_sel; + logic [VC_NUM-1:0] vc_data_freelist_deq_rdy; + logic [VC_NUM-1:0][VC_DPRAM_DEPTH_IDX_W-1:0] vc_data_freelist_deq_dpram_idx; + logic [VC_DPRAM_DEPTH_IDX_W-1:0] vc_data_freelist_deq_dpram_idx_sel; + logic [VC_NUM-1:0] vc_data_freelist_deq_vld; + + logic [VC_BUFFER_DEPTH_IDX_W-1:0] vc_data_freelist_enq_per_vc_idx; +`endif + +// enqueue +always_comb begin + vc_data_tail_we = '0; + vc_ctrl_tail_we = '0; + if(flit_v_i) begin + vc_data_tail_we[flit_vc_id_i] = 1'b1; + vc_ctrl_tail_we[flit_vc_id_i] = 1'b1; + end +end + +assign vc_data_din = flit_i; +assign vc_ctrl_din.tgt_id = flit_dec_i.tgt_id; +assign vc_ctrl_din.src_id = flit_dec_i.src_id; +assign vc_ctrl_din.txn_id = flit_dec_i.txn_id; +assign vc_ctrl_din.look_ahead_routing = flit_dec_i.look_ahead_routing; +`ifdef USE_QOS_VALUE +assign vc_ctrl_din.qos_value = flit_dec_i.qos_value; +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM +assign vc_ctrl_din.dpram_used_idx.dpram_idx = {{(VC_DPRAM_DEPTH_MAX_W-VC_DPRAM_DEPTH_IDX_W){1'b0}}, vc_data_freelist_deq_dpram_idx_sel}; +assign vc_ctrl_din.dpram_used_idx.per_vc_idx = {{(VC_BUFFER_DEPTH_MAX_W-VC_BUFFER_DEPTH_IDX_W){1'b0}}, vc_data_freelist_deq_per_vc_idx_sel}; +`endif + +// dequeue + // ctrl dequeue at SA stage +always_comb begin + vc_ctrl_head_dequeue_vld = '0; + vc_ctrl_head_dequeue_vld[inport_read_vc_id_sa_stage_i] = inport_read_enable_sa_stage_i; +end + + // data dequeue at ST stage +always_comb begin + vc_data_head_dequeue_vld = '0; + vc_data_head_dequeue_vld[inport_read_vc_id_st_stage_i] = inport_read_enable_st_stage_i; +end + + +// fifo module +`ifdef VC_DATA_USE_DUAL_PORT_RAM + generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_data_freelist_deq_dpram_idx + assign vc_data_freelist_deq_dpram_idx[i] = VC_BUFFER_DEPTH * i + vc_data_freelist_deq_per_vc_idx[i]; + end + endgenerate + + assign vc_data_freelist_deq_vld = vc_data_tail_we; + onehot_mux + #( + .SOURCE_COUNT(VC_NUM ), + .DATA_WIDTH (VC_DPRAM_DEPTH_IDX_W ) + ) + onehot_mux_vc_data_freelist_deq_dpram_idx_sel_u ( + .sel_i (vc_data_tail_we ), + .data_i (vc_data_freelist_deq_dpram_idx ), + .data_o (vc_data_freelist_deq_dpram_idx_sel ) + ); + + onehot_mux + #( + .SOURCE_COUNT(VC_NUM ), + .DATA_WIDTH (VC_BUFFER_DEPTH_IDX_W ) + ) + onehot_mux_vc_data_freelist_deq_per_vc_idx_sel_u ( + .sel_i (vc_data_tail_we ), + .data_i (vc_data_freelist_deq_per_vc_idx ), + .data_o (vc_data_freelist_deq_per_vc_idx_sel ) + ); + + + assign vc_data_freelist_enq_per_vc_idx = inport_read_dpram_idx_i.per_vc_idx[VC_BUFFER_DEPTH_IDX_W-1:0]; + + // free list + generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_data_freelist + freelist + #( + .ENTRY_COUNT (VC_BUFFER_DEPTH) + ) + VC_DATA_FREELIST_U ( + .enq_vld_i (vc_ctrl_head_dequeue_vld [i] ), + .enq_tag_i (vc_data_freelist_enq_per_vc_idx ), + .deq_vld_i (vc_data_freelist_deq_vld [i] ), + .deq_tag_o (vc_data_freelist_deq_per_vc_idx [i] ), + .deq_rdy_o (vc_data_freelist_deq_rdy [i] ), + .flush_i ('0 ), + .clk (clk ), + .rst (~rstn ) + ); + end + endgenerate + + // dpram + simple_dual_one_clock + #( + .ADDR_BITS (VC_DPRAM_DEPTH_IDX_W), + .DATA_BITS ($bits(flit_payload_t)) + ) + VC_DATA_DPRAM_U ( + .clk (clk ), + .ena (flit_v_i ), // in flit write + .enb (inport_read_enable_sa_stage_i ), // out flit read + .wea (flit_v_i ), + .addra (vc_data_freelist_deq_dpram_idx_sel ), + .addrb (inport_read_dpram_idx_i.dpram_idx[VC_DPRAM_DEPTH_IDX_W-1:0] ), + .dia (vc_data_din ), + .dob (vc_data_head_o [0] ) + ); + +`else + generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_data_fifo + mp_fifo + #( + .payload_t (flit_payload_t), + .ENQUEUE_WIDTH (1), + .DEQUEUE_WIDTH (1), + .DEPTH (VC_BUFFER_DEPTH), + .MUST_TAKEN_ALL (1) + ) + VC_DATA_U + ( + // Enqueue + .enqueue_vld_i (vc_data_tail_we [i] ), + .enqueue_payload_i (vc_data_din ), + .enqueue_rdy_o (vc_data_enqueue_rdy [i] ), + // Dequeue + .dequeue_vld_o (vc_data_head_vld [i] ), + .dequeue_payload_o (vc_data_head [i] ), + .dequeue_rdy_i (vc_data_head_dequeue_vld [i] ), + + .flush_i (1'b0 ), + + .clk (clk), + .rst (~rstn) + ); + + assign vc_data_head_o [i] = vc_data_head [i]; + end + endgenerate +`endif + + + +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_ctrl_fifo + mp_fifo + #( + .payload_t (flit_dec_t), + .ENQUEUE_WIDTH (1), + .DEQUEUE_WIDTH (1), + .DEPTH (VC_DEPTH), + .MUST_TAKEN_ALL (1) + ) + VC_CTRL_U + ( + // Enqueue + .enqueue_vld_i (vc_ctrl_tail_we [i] ), + .enqueue_payload_i (vc_ctrl_din ), + .enqueue_rdy_o (vc_ctrl_enqueue_rdy [i] ), + // Dequeue + .dequeue_vld_o (vc_ctrl_head_vld [i] ), + .dequeue_payload_o (vc_ctrl_head [i] ), + .dequeue_rdy_i (vc_ctrl_head_dequeue_vld [i] ), + + .flush_i (1'b0 ), + + .clk (clk), + .rst (~rstn) + ); + + assign vc_ctrl_head_vld_o [i] = vc_ctrl_head_vld[i]; + assign vc_ctrl_head_o [i] = vc_ctrl_head [i]; + end +endgenerate + + // free vc credit sent to sender +`ifdef RETURN_CREDIT_AT_SA_STAGE + assign lcrd_v_o = inport_read_enable_sa_stage_i; + assign lcrd_id_o = {{(VC_ID_NUM_MAX_W-VC_NUM_IDX_W){1'b0}}, inport_read_vc_id_sa_stage_i}; +`else + assign lcrd_v_o = inport_read_enable_st_stage_i; + assign lcrd_id_o = {{(VC_ID_NUM_MAX_W-VC_NUM_IDX_W){1'b0}}, inport_read_vc_id_st_stage_i}; +`endif + + + +`ifndef SYNTHESIS +`ifdef COMMON_QOS_EXTRA_RT_VC +// assert property(@(posedge clk)disable iff(~rstn) (flit_v_i == 1'b1) |-> (((flit_vc_id_i < QOS_VC_NUM_PER_INPUT) && (flit_dec_i.qos_value == 15)))) +// else $error("noc_input_port: flit with highest QoS value goes into common VC, txn_id: 0x%x, vc_id: %d, flit_v_i=%d", flit_dec_i.txn_id, flit_vc_id_i, flit_v_i); +// assert property(@(posedge clk)disable iff(~rstn) (flit_v_i == 1'b1) |-> (((flit_vc_id_i >= QOS_VC_NUM_PER_INPUT) && (flit_dec_i.qos_value < 15)))) +// else begin $display("noc_input_port: flit with lower QoS value goes into rt VC, txn_id: 0x%x, vc_id: %d, flit_v_i=%d", flit_dec_i.txn_id, flit_vc_id_i, flit_v_i); $fatal(); end + +generate + for(i = 0; i < QOS_VC_NUM_PER_INPUT; i++) begin + assert property(@(posedge clk)disable iff(~rstn) (vc_ctrl_head_vld[i]) |-> (vc_ctrl_head[i].qos_value == 15)) + else $fatal("noc_input_vc: rt VC has flit with lower QoS value, txn_id: 0x%x", vc_ctrl_head[i].txn_id); + end + for(i = QOS_VC_NUM_PER_INPUT; i < VC_NUM; i++) begin + assert property(@(posedge clk)disable iff(~rstn) (vc_ctrl_head_vld[i]) |-> (vc_ctrl_head[i].qos_value != 15)) + else $fatal("noc_input_vc: common VC has flit with highest QoS value, txn_id: 0x%x", vc_ctrl_head[i].txn_id); + end +endgenerate +`endif +`endif + +endmodule diff --git a/rtl/input_to_output.sv b/rtl/input_to_output.sv new file mode 100644 index 0000000..9805e93 --- /dev/null +++ b/rtl/input_to_output.sv @@ -0,0 +1,516 @@ +// this module connects switch allocation stage to switch traversal stage +// NOTE: if change to sram as input buffer, the read should conduct right after local allocate +module input_to_output +import rvh_noc_pkg::*; +#( + parameter INPUT_PORT_NUM = 5, + parameter OUTPUT_PORT_NUM = 5, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + + parameter SA_GLOBAL_INPUT_NUM_N = 4, + parameter SA_GLOBAL_INPUT_NUM_S = 4, + parameter SA_GLOBAL_INPUT_NUM_E = 2, + parameter SA_GLOBAL_INPUT_NUM_W = 2, + parameter SA_GLOBAL_INPUT_NUM_L = 4, + parameter SA_GLOBAL_INPUT_NUM_N_W = SA_GLOBAL_INPUT_NUM_N > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_N) : 1, + parameter SA_GLOBAL_INPUT_NUM_S_W = SA_GLOBAL_INPUT_NUM_S > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_S) : 1, + parameter SA_GLOBAL_INPUT_NUM_E_W = SA_GLOBAL_INPUT_NUM_E > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_E) : 1, + parameter SA_GLOBAL_INPUT_NUM_W_W = SA_GLOBAL_INPUT_NUM_W > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_W) : 1, + parameter SA_GLOBAL_INPUT_NUM_L_W = SA_GLOBAL_INPUT_NUM_L > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_L) : 1 + +) +( + // input from sa global allocation + input logic [OUTPUT_PORT_NUM-1:0] sa_global_vld_i, + // input logic [OUTPUT_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_MAX_W-1:0] sa_global_inport_id_i, + input logic [OUTPUT_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_MAX-1:0] sa_global_inport_id_oh_i, + input logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] sa_global_inport_vc_id_i, + + // input from vc allocation + input logic [OUTPUT_PORT_NUM-1:0] vc_assignment_vld_i, + input logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] vc_assignment_vc_id_i, + input io_port_t [OUTPUT_PORT_NUM-1:0] look_ahead_routing_sel_i, + + // output to input port buffer to get selected flit + output logic [INPUT_PORT_NUM-1:0] inport_read_enable_o, + // output io_port_t [INPUT_PORT_NUM-1:0] inport_read_outport_id_o, + output logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] inport_read_vc_id_o, + // output io_port_t [INPUT_PORT_NUM-1:0] inport_look_ahead_routing_o, + + // output to switch to let outport select inport + output logic [OUTPUT_PORT_NUM-1:0] outport_vld_o, + output io_port_t [OUTPUT_PORT_NUM-1:0] outport_select_inport_id_o, + output logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] outport_vc_id_o, + output io_port_t [OUTPUT_PORT_NUM-1:0] outport_look_ahead_routing_o, + + // output to outport vc credit counter to consume one credit + output logic [OUTPUT_PORT_NUM-1:0] consume_vc_credit_vld_o, + output logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] consume_vc_credit_vc_id_o +); + +genvar i, j, k; + +io_port_t [OUTPUT_PORT_NUM-1:0] inport_id_per_outport; +logic [OUTPUT_PORT_NUM-1:0][INPUT_PORT_NUM-1:0] inport_id_oh_per_outport; +logic [INPUT_PORT_NUM-1:0][OUTPUT_PORT_NUM-1:0] outport_id_oh_per_inport; + +// outports valid signal +generate + for(i = 0; i < OUTPUT_PORT_NUM; i++) begin: gen_consume_vc_credit + assign consume_vc_credit_vld_o [i] = sa_global_vld_i[i] & vc_assignment_vld_i[i]; + assign consume_vc_credit_vc_id_o[i] = vc_assignment_vc_id_i[i]; + end +endgenerate + +assign outport_vld_o = consume_vc_credit_vld_o; +assign outport_select_inport_id_o = inport_id_per_outport; +assign outport_vc_id_o = vc_assignment_vc_id_i; +assign outport_look_ahead_routing_o = look_ahead_routing_sel_i; + +// map outports to per inport + // inport_read_enable_o +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + for(j = 0; j < OUTPUT_PORT_NUM; j++) begin + assign outport_id_oh_per_inport[i][j] = inport_id_oh_per_outport[j][i]; + end + end +endgenerate + + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + assign inport_read_enable_o[i] = |(outport_id_oh_per_inport[i] & vc_assignment_vld_i); + end +endgenerate + + // inport_read_vc_id_o +logic [OUTPUT_PORT_NUM-1:0][INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] inport_vc_id_oh_per_outport; +logic [INPUT_PORT_NUM-1:0][OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] outport_vc_id_oh_per_inport; +logic [INPUT_PORT_NUM*VC_ID_NUM_MAX_W-1:0][OUTPUT_PORT_NUM-1:0] outport_vc_id_oh_per_inport_mid1; +logic [INPUT_PORT_NUM*VC_ID_NUM_MAX_W-1:0] outport_vc_id_oh_per_inport_mid2; + +generate + for(i = 0; i < OUTPUT_PORT_NUM; i++) begin + for(j = 0; j < INPUT_PORT_NUM; j++) begin + assign inport_vc_id_oh_per_outport[i][j] = {VC_ID_NUM_MAX_W{inport_id_oh_per_outport[i][j]}} & sa_global_inport_vc_id_i[i]; + end + end +endgenerate + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + for(j = 0; j < OUTPUT_PORT_NUM; j++) begin + assign outport_vc_id_oh_per_inport[i][j] = inport_vc_id_oh_per_outport[j][i]; + end + end +endgenerate + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + for(j = 0; j < OUTPUT_PORT_NUM; j++) begin + for(k = 0; k < VC_ID_NUM_MAX_W; k++) begin + assign outport_vc_id_oh_per_inport_mid1[i*VC_ID_NUM_MAX_W+k][j] = outport_vc_id_oh_per_inport[i][j][k]; + end + end + end +endgenerate + +generate + for(i = 0; i < INPUT_PORT_NUM*VC_ID_NUM_MAX_W; i++) begin + assign outport_vc_id_oh_per_inport_mid2[i] = |(outport_vc_id_oh_per_inport_mid1[i]); + end +endgenerate + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + for(j = 0; j < VC_ID_NUM_MAX_W; j++) begin + assign inport_read_vc_id_o[i][j] = outport_vc_id_oh_per_inport_mid2[i*VC_ID_NUM_MAX_W+j]; + end + end +endgenerate + + + + +// always_comb begin +// // inport_read_enable_o = '0; +// inport_read_vc_id_o = '0; +// for(int i = 0; i < INPUT_PORT_NUM; i++) begin +// for(int j = 0; j < OUTPUT_PORT_NUM; j++) begin +// if((inport_id_per_outport[j] == i[$bits(io_port_t)-1:0]) & consume_vc_credit_vld_o[j]) begin +// // inport_read_enable_o [i] = 1'b1; +// // inport_read_outport_id_o [i] = inport_id_per_outport[j]; +// inport_read_vc_id_o [i] = sa_global_inport_vc_id_i[j]; +// // inport_look_ahead_routing_o [i] = look_ahead_routing_sel_i[j]; +// end +// end +// end +// end + +// map sa global input no. to router inport no. + // to N +always_comb begin + inport_id_per_outport[0] = S; + inport_id_oh_per_outport[0] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[0][0]: begin + inport_id_per_outport[0] = S; + inport_id_oh_per_outport[0][1] = 1'b1; + end + sa_global_inport_id_oh_i[0][1]: begin + inport_id_per_outport[0] = E; + inport_id_oh_per_outport[0][2] = 1'b1; + end + sa_global_inport_id_oh_i[0][2]: begin + inport_id_per_outport[0] = W; + inport_id_oh_per_outport[0][3] = 1'b1; + end + sa_global_inport_id_oh_i[0][3]: begin + inport_id_per_outport[0] = L0; + inport_id_oh_per_outport[0][4] = 1'b1; + end +`ifdef LOCAL_PORT_NUM_2 + sa_global_inport_id_oh_i[0][4]: begin + inport_id_per_outport[0] = L1; + inport_id_oh_per_outport[0][5] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[0][5]: begin + inport_id_per_outport[0] = L2; + inport_id_oh_per_outport[0][6] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[0][6]: begin + inport_id_per_outport[0] = L3; + inport_id_oh_per_outport[0][7] = 1'b1; + end +`endif + default: begin + end + endcase +end + + // to S +always_comb begin + inport_id_per_outport[1] = N; + inport_id_oh_per_outport[1] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[1][0]: begin + inport_id_per_outport[1] = N; + inport_id_oh_per_outport[1][0] = 1'b1; + end + sa_global_inport_id_oh_i[1][1]: begin + inport_id_per_outport[1] = E; + inport_id_oh_per_outport[1][2] = 1'b1; + end + sa_global_inport_id_oh_i[1][2]: begin + inport_id_per_outport[1] = W; + inport_id_oh_per_outport[1][3] = 1'b1; + end + sa_global_inport_id_oh_i[1][3]: begin + inport_id_per_outport[1] = L0; + inport_id_oh_per_outport[1][4] = 1'b1; + end +`ifdef LOCAL_PORT_NUM_2 + sa_global_inport_id_oh_i[1][4]: begin + inport_id_per_outport[1] = L1; + inport_id_oh_per_outport[1][5] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[1][5]: begin + inport_id_per_outport[1] = L2; + inport_id_oh_per_outport[1][6] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[1][6]: begin + inport_id_per_outport[1] = L3; + inport_id_oh_per_outport[1][7] = 1'b1; + end +`endif + default: begin + end + endcase +end + + // to E +always_comb begin + inport_id_per_outport[2] = W; + inport_id_oh_per_outport[2] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[2][0]: begin + inport_id_per_outport[2] = W; + inport_id_oh_per_outport[2][3] = 1'b1; + end + sa_global_inport_id_oh_i[2][1]: begin + inport_id_per_outport[2] = L0; + inport_id_oh_per_outport[2][4] = 1'b1; + end +`ifdef LOCAL_PORT_NUM_2 + sa_global_inport_id_oh_i[2][2]: begin + inport_id_per_outport[2] = L1; + inport_id_oh_per_outport[2][5] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[2][3]: begin + inport_id_per_outport[2] = L2; + inport_id_oh_per_outport[2][6] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[2][4]: begin + inport_id_per_outport[2] = L3; + inport_id_oh_per_outport[2][7] = 1'b1; + end +`endif + default: begin + end + endcase +end + + // to W +always_comb begin + inport_id_per_outport[3] = E; + inport_id_oh_per_outport[3] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[3][0]: begin + inport_id_per_outport[3] = E; + inport_id_oh_per_outport[3][2] = 1'b1; + end + sa_global_inport_id_oh_i[3][1]: begin + inport_id_per_outport[3] = L0; + inport_id_oh_per_outport[3][4] = 1'b1; + end +`ifdef LOCAL_PORT_NUM_2 + sa_global_inport_id_oh_i[3][2]: begin + inport_id_per_outport[3] = L1; + inport_id_oh_per_outport[3][5] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[3][3]: begin + inport_id_per_outport[3] = L2; + inport_id_oh_per_outport[3][6] = 1'b1; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[3][4]: begin + inport_id_per_outport[3] = L3; + inport_id_oh_per_outport[3][7] = 1'b1; + end +`endif + default: begin + end + endcase +end + + // to L +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER // allow local to local transfer, at least 2 local ports per router + // to L0 +always_comb begin + inport_id_per_outport[4+0] = N; + inport_id_oh_per_outport[4+0] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[4+0][0]: begin + inport_id_per_outport[4+0] = N; + inport_id_oh_per_outport[4+0][0] = 1'b1; + end + sa_global_inport_id_oh_i[4+0][1]: begin + inport_id_per_outport[4+0] = S; + inport_id_oh_per_outport[4+0][1] = 1'b1; + end + sa_global_inport_id_oh_i[4+0][2]: begin + inport_id_per_outport[4+0] = E; + inport_id_oh_per_outport[4+0][2] = 1'b1; + end + sa_global_inport_id_oh_i[4+0][3]: begin + inport_id_per_outport[4+0] = W; + inport_id_oh_per_outport[4+0][3] = 1'b1; + end + sa_global_inport_id_oh_i[4+0][4]: begin + inport_id_per_outport[4+0] = L1; + inport_id_oh_per_outport[4+0][5] = 1'b1; + end + `ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[4+0][5]: begin + inport_id_per_outport[4+0] = L2; + inport_id_oh_per_outport[4+0][6] = 1'b1; + end + `endif + `ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[4+0][6]: begin + inport_id_per_outport[4+0] = L3; + inport_id_oh_per_outport[4+0][7] = 1'b1; + end + `endif + default: begin + end + endcase +end + + // to L1 +always_comb begin + inport_id_per_outport[4+1] = N; + inport_id_oh_per_outport[4+1] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[4+1][0]: begin + inport_id_per_outport[4+1] = N; + inport_id_oh_per_outport[4+1][0] = 1'b1; + end + sa_global_inport_id_oh_i[4+1][1]: begin + inport_id_per_outport[4+1] = S; + inport_id_oh_per_outport[4+1][1] = 1'b1; + end + sa_global_inport_id_oh_i[4+1][2]: begin + inport_id_per_outport[4+1] = E; + inport_id_oh_per_outport[4+1][2] = 1'b1; + end + sa_global_inport_id_oh_i[4+1][3]: begin + inport_id_per_outport[4+1] = W; + inport_id_oh_per_outport[4+1][3] = 1'b1; + end + sa_global_inport_id_oh_i[4+1][4]: begin + inport_id_per_outport[4+1] = L0; + inport_id_oh_per_outport[4+1][4] = 1'b1; + end + `ifdef LOCAL_PORT_NUM_3 + sa_global_inport_id_oh_i[4+1][5]: begin + inport_id_per_outport[4+1] = L2; + inport_id_oh_per_outport[4+1][6] = 1'b1; + end + `endif + `ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[4+1][6]: begin + inport_id_per_outport[4+1] = L3; + inport_id_oh_per_outport[4+1][7] = 1'b1; + end + `endif + default: begin + end + endcase +end + + + `ifdef LOCAL_PORT_NUM_3 + // to L2 + always_comb begin + inport_id_per_outport[4+2] = N; + inport_id_oh_per_outport[4+2] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[4+2][0]: begin + inport_id_per_outport[4+2] = N; + inport_id_oh_per_outport[4+2][0] = 1'b1; + end + sa_global_inport_id_oh_i[4+2][1]: begin + inport_id_per_outport[4+2] = S; + inport_id_oh_per_outport[4+2][1] = 1'b1; + end + sa_global_inport_id_oh_i[4+2][2]: begin + inport_id_per_outport[4+2] = E; + inport_id_oh_per_outport[4+2][2] = 1'b1; + end + sa_global_inport_id_oh_i[4+2][3]: begin + inport_id_per_outport[4+2] = W; + inport_id_oh_per_outport[4+2][3] = 1'b1; + end + sa_global_inport_id_oh_i[4+2][4]: begin + inport_id_per_outport[4+2] = L0; + inport_id_oh_per_outport[4+2][4] = 1'b1; + end + sa_global_inport_id_oh_i[4+2][5]: begin + inport_id_per_outport[4+2] = L1; + inport_id_oh_per_outport[4+2][5] = 1'b1; + end + `ifdef LOCAL_PORT_NUM_4 + sa_global_inport_id_oh_i[4+2][6]: begin + inport_id_per_outport[4+2] = L3; + inport_id_oh_per_outport[4+2][7] = 1'b1; + end + `endif + default: begin + end + endcase + end + `endif + + `ifdef LOCAL_PORT_NUM_4 + // to L3 + always_comb begin + inport_id_per_outport[4+3] = N; + inport_id_oh_per_outport[4+3] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[4+3][0]: begin + inport_id_per_outport[4+3] = N; + inport_id_oh_per_outport[4+3][0] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][1]: begin + inport_id_per_outport[4+3] = S; + inport_id_oh_per_outport[4+3][1] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][2]: begin + inport_id_per_outport[4+3] = E; + inport_id_oh_per_outport[4+3][2] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][3]: begin + inport_id_per_outport[4+3] = W; + inport_id_oh_per_outport[4+3][3] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][4]: begin + inport_id_per_outport[4+3] = L0; + inport_id_oh_per_outport[4+3][4] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][5]: begin + inport_id_per_outport[4+3] = L1; + inport_id_oh_per_outport[4+3][5] = 1'b1; + end + sa_global_inport_id_oh_i[4+3][6]: begin + inport_id_per_outport[4+3] = L2; + inport_id_oh_per_outport[4+3][6] = 1'b1; + end + default: begin + end + endcase + end + `endif + + +`else +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_multi_local_port + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_multi_local_port + always_comb begin + inport_id_per_outport[4+i] = N; + inport_id_oh_per_outport[4+i] = '0; + unique case(1'b1) + sa_global_inport_id_oh_i[4+i][0]: begin + inport_id_per_outport[4+i] = N; + inport_id_oh_per_outport[4+i][0] = 1'b1; + end + sa_global_inport_id_oh_i[4+i][1]: begin + inport_id_per_outport[4+i] = S; + inport_id_oh_per_outport[4+i][1] = 1'b1; + end + sa_global_inport_id_oh_i[4+i][2]: begin + inport_id_per_outport[4+i] = E; + inport_id_oh_per_outport[4+i][2] = 1'b1; + end + sa_global_inport_id_oh_i[4+i][3]: begin + inport_id_per_outport[4+i] = W; + inport_id_oh_per_outport[4+i][3] = 1'b1; + end + default: begin + end + endcase + end + end + end +endgenerate +`endif + + +endmodule diff --git a/rtl/local_port_couple_module.sv b/rtl/local_port_couple_module.sv new file mode 100644 index 0000000..053c22f --- /dev/null +++ b/rtl/local_port_couple_module.sv @@ -0,0 +1,329 @@ +// this module is at local device side, use to handle local device credit based flow control +module local_port_couple_module +import rvh_noc_pkg::*; +#( + parameter VC_NUM_OUTPORT = 2, + parameter VC_NUM_OUTPORT_IDX_W = VC_NUM_OUTPORT > 1 ? $clog2(VC_NUM_OUTPORT) : 1, + parameter VC_DEPTH_OUTPORT = 2, + parameter VC_DEPTH_OUTPORT_COUNTER_W = $clog2(VC_DEPTH_OUTPORT + 1), + + // for local ports, always OUTPUT_TO_L + parameter OUTPUT_TO_N = 0, + parameter OUTPUT_TO_S = 0, + parameter OUTPUT_TO_E = 0, + parameter OUTPUT_TO_W = 0, + parameter OUTPUT_TO_L = 1 +) +( + // input vc head to calculate next routing + input logic [NodeID_X_Width-1:0] node_id_x_tgt_i, + input logic [NodeID_Y_Width-1:0] node_id_y_tgt_i, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + input logic [NodeID_Device_Port_Width-1:0] device_port_tgt_i, +`endif + + // input this hop xy addr + input logic [NodeID_X_Width-1:0] node_id_x_src_i, + input logic [NodeID_Y_Width-1:0] node_id_y_src_i, + + // output look ahead routing result + output io_port_t look_ahead_routing_o, + + // free credit in from router + input logic tx_lcrd_v_i, + input logic [VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i, + + // consume credit + input logic flit_vld_i, + input logic [QoS_Value_Width-1:0] flit_qos_value_i, + output logic free_credit_vld_o, + output logic [VC_NUM_OUTPORT_IDX_W-1:0] free_credit_vc_id_o, + + input logic clk, + input logic rstn +); + +logic [VC_NUM_OUTPORT-1:0][VC_DEPTH_OUTPORT_COUNTER_W-1:0] vc_credit_counter; +logic [VC_NUM_OUTPORT-1:0] vc_credit_counter_non_zero; +logic [VC_NUM_OUTPORT-QOS_VC_NUM_PER_INPUT-1:0] vc_allocate_common_vc_grt_oh; +logic [$clog2(VC_NUM_OUTPORT-QOS_VC_NUM_PER_INPUT)-1:0] vc_allocate_common_vc_grt_idx; +`ifdef COMMON_QOS_EXTRA_RT_VC +logic [QOS_VC_NUM_PER_INPUT-1:0] vc_allocate_rt_vc_grt_oh; +logic [$clog2(QOS_VC_NUM_PER_INPUT)-1:0] vc_allocate_rt_vc_grt_idx; +`else +logic [1-1:0] vc_allocate_rt_vc_grt_oh; +logic [$clog2(1)-1:0] vc_allocate_rt_vc_grt_idx; +`endif + +logic [VC_NUM_OUTPORT_IDX_W-1:0] preferred_vc_id; + +// to send flit buffer +logic flit_buffer_head_rt_vc_en; + +logic free_credit_vld; +logic flit_buffer_dequeue_vld; + +logic consume_vc_credit_vld; +logic [VC_NUM_OUTPORT_IDX_W-1:0] consume_vc_credit_vc_id; + + +// 1st phase: calculate look ahead routing +local_port_look_adead_routing +local_port_look_adead_routing_u +( + .node_id_x_tgt_i (node_id_x_tgt_i ), + .node_id_y_tgt_i (node_id_y_tgt_i ), +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + .device_port_tgt_i (device_port_tgt_i), +`endif + .node_id_x_src_i (node_id_x_src_i ), + .node_id_y_src_i (node_id_y_src_i ), + .look_ahead_routing_o ( look_ahead_routing_o) +); + +// 2nd phase: choose input vc + +assign flit_buffer_head_rt_vc_en = QOS_VC_NUM_PER_INPUT && (flit_qos_value_i == '1); + +assign free_credit_vld = ( +`ifdef COMMON_QOS_EXTRA_RT_VC + (flit_buffer_head_rt_vc_en & (|(vc_credit_counter_non_zero[QOS_VC_NUM_PER_INPUT-1:0]))) | +`endif + (~flit_buffer_head_rt_vc_en & (|(vc_credit_counter_non_zero[VC_NUM_OUTPORT-1:QOS_VC_NUM_PER_INPUT]))) + ); + +assign free_credit_vld_o = free_credit_vld; +assign free_credit_vc_id_o = consume_vc_credit_vc_id; + +assign flit_buffer_dequeue_vld = flit_vld_i & // head valid + free_credit_vld; // vc not empty + +// rr for vc allocation +generate + for(genvar i = 0; i < VC_NUM_OUTPORT; i++) begin: gen_vc_credit_counter_non_zero + assign vc_credit_counter_non_zero[i] = ~(vc_credit_counter[i] == '0); + end +endgenerate + +one_hot_rr_arb #( + .N_INPUT (VC_NUM_OUTPORT-QOS_VC_NUM_PER_INPUT) +) +vc_allocate_common_vc_rr_arb_u +( + .req_i (vc_credit_counter_non_zero[VC_NUM_OUTPORT-1:QOS_VC_NUM_PER_INPUT] ), + .update_i (|(vc_credit_counter_non_zero[VC_NUM_OUTPORT-1:QOS_VC_NUM_PER_INPUT])), + .grt_o (vc_allocate_common_vc_grt_oh ), + .grt_idx_o (vc_allocate_common_vc_grt_idx ), + + .rstn (rstn ), + .clk (clk ) +); + +`ifdef COMMON_QOS_EXTRA_RT_VC + generate + if(QOS_VC_NUM_PER_INPUT > 1) begin: gen_vc_allocate_rt_vc_rr_arb_u + one_hot_rr_arb #( + .N_INPUT (QOS_VC_NUM_PER_INPUT) + ) + vc_allocate_rt_vc_rr_arb_u + ( + .req_i (vc_credit_counter_non_zero[QOS_VC_NUM_PER_INPUT-1:0] ), + .update_i (|(vc_credit_counter_non_zero[QOS_VC_NUM_PER_INPUT-1:0])), + .grt_o (vc_allocate_rt_vc_grt_oh ), + .grt_idx_o (vc_allocate_rt_vc_grt_idx ), + + .rstn (rstn ), + .clk (clk ) + ); + end else begin + assign vc_allocate_rt_vc_grt_oh = vc_credit_counter_non_zero[0]; + assign vc_allocate_rt_vc_grt_idx = '0; + end + endgenerate +`else +assign vc_allocate_rt_vc_grt_oh = '0; +assign vc_allocate_rt_vc_grt_idx = '0; +`endif + +// credit counter +assign consume_vc_credit_vld = flit_buffer_dequeue_vld; +assign consume_vc_credit_vc_id = flit_buffer_head_rt_vc_en ? vc_allocate_rt_vc_grt_idx : + vc_credit_counter_non_zero[preferred_vc_id] ? preferred_vc_id : + vc_allocate_common_vc_grt_idx+QOS_VC_NUM_PER_INPUT; + +output_port_vc_credit_counter +#( + .VC_NUM (VC_NUM_OUTPORT ), + .VC_DEPTH (VC_DEPTH_OUTPORT ) +) +output_port_vc_credit_counter_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i ), + .consume_vc_credit_vld_i (consume_vc_credit_vld ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id ), + .vc_credit_counter_o (vc_credit_counter ), + .clk (clk ), + .rstn (rstn ) +); + + +// select preferred vc id for different output ports +// to N vc: +// vc0 +generate + + if(OUTPUT_TO_N) begin: gen_output_to_n // output to N, next hop input is S + always_comb begin + preferred_vc_id = '0; + unique case(look_ahead_routing_o) + S: begin + preferred_vc_id = 0; + end + L0: begin + preferred_vc_id = 1; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + preferred_vc_id = 2; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + preferred_vc_id = 3; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + preferred_vc_id = 4; + end +`endif + default: begin + end + endcase + preferred_vc_id = preferred_vc_id + QOS_VC_NUM_PER_INPUT; + end + end + + if(OUTPUT_TO_S) begin: gen_output_to_s // output to S, next hop input is N + always_comb begin + preferred_vc_id = '0; + unique case(look_ahead_routing_o) + N: begin + preferred_vc_id = 0; + end + L0: begin + preferred_vc_id = 1; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + preferred_vc_id = 2; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + preferred_vc_id = 3; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + preferred_vc_id = 4; + end +`endif + default: begin + end + endcase + preferred_vc_id = preferred_vc_id + QOS_VC_NUM_PER_INPUT; + end + end + + if(OUTPUT_TO_E) begin: gen_output_to_e // output to E, next hop input is W + always_comb begin + preferred_vc_id = '0; + unique case(look_ahead_routing_o) + N: begin + preferred_vc_id = 0; + end + S: begin + preferred_vc_id = 1; + end + W: begin + preferred_vc_id = 2; + end + L0: begin + preferred_vc_id = 3; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + preferred_vc_id = 4; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + preferred_vc_id = 5; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + preferred_vc_id = 6; + end +`endif + default: begin + end + endcase + preferred_vc_id = preferred_vc_id + QOS_VC_NUM_PER_INPUT; + end + end + + if(OUTPUT_TO_W) begin: gen_output_to_w // output to W, next hop input is E + always_comb begin + preferred_vc_id = '0; + unique case(look_ahead_routing_o) + N: begin + preferred_vc_id = 0; + end + S: begin + preferred_vc_id = 1; + end + E: begin + preferred_vc_id = 2; + end + L0: begin + preferred_vc_id = 3; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + preferred_vc_id = 4; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + preferred_vc_id = 5; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + preferred_vc_id = 6; + end +`endif + default: begin + end + endcase + preferred_vc_id = preferred_vc_id + QOS_VC_NUM_PER_INPUT; + end + end + + if(OUTPUT_TO_L) begin: gen_output_to_l // output to L, no next hop + always_comb begin + preferred_vc_id = '0; + unique case(look_ahead_routing_o) // assume all local port have 1 vc + default: begin + preferred_vc_id = '0; + end + endcase + preferred_vc_id = preferred_vc_id + QOS_VC_NUM_PER_INPUT; + end + end + +endgenerate + +endmodule diff --git a/rtl/local_port_look_adead_routing.sv b/rtl/local_port_look_adead_routing.sv new file mode 100644 index 0000000..bd510db --- /dev/null +++ b/rtl/local_port_look_adead_routing.sv @@ -0,0 +1,93 @@ +// implemented XY routing, for per local sa winnner flit +module local_port_look_adead_routing + import rvh_noc_pkg::*; +#( +) +( + // input vc head to calculate next routing + input logic [NodeID_X_Width-1:0] node_id_x_tgt_i, + input logic [NodeID_Y_Width-1:0] node_id_y_tgt_i, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + input logic [NodeID_Device_Port_Width-1:0] device_port_tgt_i, +`endif + + // input this hop xy addr + input logic [NodeID_X_Width-1:0] node_id_x_src_i, + input logic [NodeID_Y_Width-1:0] node_id_y_src_i, + + // output look ahead routing result + output io_port_t look_ahead_routing_o +); + +// 2nd phase: Define new Next-port +logic x_nxt_equal_x_dst; +logic x_nxt_less_x_dst; +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER +logic y_nxt_equal_y_dst; +`endif +logic y_nxt_less_y_dst; + +assign x_nxt_equal_x_dst = (node_id_x_src_i == node_id_x_tgt_i); +assign x_nxt_less_x_dst = (node_id_x_src_i < node_id_x_tgt_i); +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER +assign y_nxt_equal_y_dst = (node_id_y_src_i == node_id_y_tgt_i); +`endif +assign y_nxt_less_y_dst = (node_id_y_src_i < node_id_y_tgt_i); + + +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER +always_comb begin + if(x_nxt_equal_x_dst) begin + if(y_nxt_equal_y_dst) begin + unique case(device_port_tgt_i) // chooose which local port to route to + 0: begin + look_ahead_routing_o = L0; + end +`ifdef LOCAL_PORT_NUM_2 + 1: begin + look_ahead_routing_o = L1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + 2: begin + look_ahead_routing_o = L2; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + 3: begin + look_ahead_routing_o = L3; + end +`endif + default: begin + look_ahead_routing_o = L0; + end + endcase + end else if(y_nxt_less_y_dst) begin + look_ahead_routing_o = N; + end else begin + look_ahead_routing_o = S; + end + end else if(x_nxt_less_x_dst) begin + look_ahead_routing_o = E; + end else begin + look_ahead_routing_o = W; + end +end + +`else +always_comb begin + if(x_nxt_equal_x_dst) begin + if(y_nxt_less_y_dst) begin + look_ahead_routing_o = N; + end else begin + look_ahead_routing_o = S; + end + end else if(x_nxt_less_x_dst) begin + look_ahead_routing_o = E; + end else begin + look_ahead_routing_o = W; + end +end +`endif + +endmodule diff --git a/rtl/look_adead_routing.sv b/rtl/look_adead_routing.sv new file mode 100644 index 0000000..ace1b10 --- /dev/null +++ b/rtl/look_adead_routing.sv @@ -0,0 +1,96 @@ +// implemented XY routing, for per local sa winnner flit +module look_ahead_routing + import rvh_noc_pkg::*; +#( +) +( + // input vc head to calculate next routing + input logic vc_ctrl_head_vld_i, + input flit_dec_t vc_ctrl_head_i, + + // input this hop xy addr + input logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i, + input logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i, + + // output look ahead routing result + output io_port_t look_ahead_routing_o +); + +logic [NodeID_X_Width-1:0] node_id_x_nxt_hop, node_id_x_dst_hop; +logic [NodeID_Y_Width-1:0] node_id_y_nxt_hop, node_id_y_dst_hop; + +assign node_id_x_dst_hop = vc_ctrl_head_i.tgt_id.x_position; +assign node_id_y_dst_hop = vc_ctrl_head_i.tgt_id.y_position; + +// 1st phase: Assign next address +always_comb begin + node_id_x_nxt_hop = node_id_x_ths_hop_i; + node_id_y_nxt_hop = node_id_y_ths_hop_i; + unique case(vc_ctrl_head_i.look_ahead_routing) + N: begin + node_id_y_nxt_hop = node_id_y_ths_hop_i + 1; + end + S: begin + node_id_y_nxt_hop = node_id_y_ths_hop_i - 1; + end + E: begin + node_id_x_nxt_hop = node_id_x_ths_hop_i + 1; + end + W: begin + node_id_x_nxt_hop = node_id_x_ths_hop_i - 1; + end + default: begin + end + endcase +end + +// 2nd phase: Define new Next-port +logic x_nxt_equal_x_dst; +logic x_nxt_less_x_dst; +logic y_nxt_equal_y_dst; +logic y_nxt_less_y_dst; + +assign x_nxt_equal_x_dst = (node_id_x_nxt_hop == node_id_x_dst_hop); +assign x_nxt_less_x_dst = (node_id_x_nxt_hop < node_id_x_dst_hop); +assign y_nxt_equal_y_dst = (node_id_y_nxt_hop == node_id_y_dst_hop); +assign y_nxt_less_y_dst = (node_id_y_nxt_hop < node_id_y_dst_hop); + +always_comb begin + if(x_nxt_equal_x_dst) begin + if(y_nxt_equal_y_dst) begin + unique case(vc_ctrl_head_i.tgt_id.device_port) // chooose which local port to route to + 0: begin + look_ahead_routing_o = L0; + end +`ifdef LOCAL_PORT_NUM_2 + 1: begin + look_ahead_routing_o = L1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + 2: begin + look_ahead_routing_o = L2; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + 3: begin + look_ahead_routing_o = L3; + end +`endif + default: begin + look_ahead_routing_o = L0; + end + endcase + end else if(y_nxt_less_y_dst) begin + look_ahead_routing_o = N; + end else begin + look_ahead_routing_o = S; + end + end else if(x_nxt_less_x_dst) begin + look_ahead_routing_o = E; + end else begin + look_ahead_routing_o = W; + end +end + +endmodule diff --git a/rtl/model/cells/rrv64_cell_clkgate.v b/rtl/model/cells/rrv64_cell_clkgate.v new file mode 100755 index 0000000..2d59514 --- /dev/null +++ b/rtl/model/cells/rrv64_cell_clkgate.v @@ -0,0 +1,30 @@ +module rrv64_cell_clkgate ( + input wire clk_i, + input wire clk_enable_i, + input wire clk_senable_i, + output wire clk_gated_o +); + +// CKGATE +// CKGATE_inst +// ( +// .cko (clk_gated_o), +// .cki (clk_i ), +// .e (clk_enable_i), +// .te (clk_senable_i), +// ); + + wire clk_en; + reg clk_en_reg; + + assign clk_en = clk_enable_i | clk_senable_i; + + always @ (clk_i or clk_en) begin + if(clk_i == 1'b0) begin + clk_en_reg <= clk_en; + end + end + + assign clk_gated_o = clk_i & clk_en_reg; + +endmodule \ No newline at end of file diff --git a/rtl/model/cells/std_dff.sv b/rtl/model/cells/std_dff.sv new file mode 100755 index 0000000..5728e63 --- /dev/null +++ b/rtl/model/cells/std_dff.sv @@ -0,0 +1,27 @@ +//=============================================== +//Name : STD_DFF +//Author : cuiluping +//Email : luping.cui@rivai-ic.com.cn +//Date : 2019-08-24 +//Description : no reset no enable D flip flop +//----------------------------------------------- +//All Rights Reserved by rivai company +//=============================================== + +module std_dff +#( + parameter WIDTH = 8 +) +( + input clk, + input [WIDTH-1:0] d, + output [WIDTH-1:0] q +); + +logic [WIDTH-1:0] dff_q; +always_ff @(posedge clk) begin + dff_q <= d; +end + +assign q = dff_q; +endmodule diff --git a/rtl/model/cells/std_dffe.sv b/rtl/model/cells/std_dffe.sv new file mode 100755 index 0000000..6a41671 --- /dev/null +++ b/rtl/model/cells/std_dffe.sv @@ -0,0 +1,31 @@ +//=============================================== +//Name : STD_DFFE +//Author : cuiluping +//Email : luping.cui@rivai-ic.com.cn +//Date : 2019-08-24 +//Description : no reset ,with enable D flip flop +//----------------------------------------------- +//All Rights Reserved by rivai company +//=============================================== + +module std_dffe +#( + parameter WIDTH = 8 +) +( + input clk, + input en, + input [WIDTH-1:0] d, + output [WIDTH-1:0] q +); + +logic [WIDTH-1:0] dff_q; + +always_ff @(posedge clk) begin + if(en) begin + dff_q <= d; + end +end +assign q = dff_q; + +endmodule diff --git a/rtl/model/cells/std_dffr.sv b/rtl/model/cells/std_dffr.sv new file mode 100755 index 0000000..089d3d7 --- /dev/null +++ b/rtl/model/cells/std_dffr.sv @@ -0,0 +1,34 @@ +//=============================================== +//Name : STD_DFFR +//Author : cuiluping +//Email : luping.cui@rivai-ic.com.cn +//Date : 2019-08-24 +//Description : with reset ,no enable D flip flop +//----------------------------------------------- +//All Rights Reserved by rivai company +//=============================================== + +module std_dffr +#( + parameter WIDTH = 8 +) +( + input clk, + input rstn, + input [WIDTH-1:0] d, + output [WIDTH-1:0] q +); + +logic [WIDTH-1:0] dff_q; + +always_ff @(posedge clk or negedge rstn) begin + if(~rstn)begin + dff_q <= {WIDTH{1'b0}}; + end + else begin + dff_q <= d; + end +end + +assign q = dff_q; +endmodule diff --git a/rtl/model/cells/std_dffre.sv b/rtl/model/cells/std_dffre.sv new file mode 100755 index 0000000..1da7a6c --- /dev/null +++ b/rtl/model/cells/std_dffre.sv @@ -0,0 +1,36 @@ +//=============================================== +//Name : STD_DFFRE +//Author : cuiluping +//Email : luping.cui@rivai-ic.com.cn +//Date : 2019-08-24 +//Description : with reset ,with enable D flip flop +//----------------------------------------------- +//All Rights Reserved by rivai company +//=============================================== + +module std_dffre +#( + parameter WIDTH = 8 +) +( + input clk, + input rstn, + input en, + input [WIDTH-1:0] d, + output [WIDTH-1:0] q +); + +logic [WIDTH-1:0] dff_q; + +always_ff @(posedge clk or negedge rstn) begin + if(~rstn)begin + dff_q <= {WIDTH{1'b0}}; + end + else if(en)begin + dff_q <= d; + end +end + +assign q = dff_q; + +endmodule diff --git a/rtl/model/cells/std_dffrve.sv b/rtl/model/cells/std_dffrve.sv new file mode 100755 index 0000000..f02ffc3 --- /dev/null +++ b/rtl/model/cells/std_dffrve.sv @@ -0,0 +1,38 @@ +//=============================================== +//Name : STD_DFFRVE +//Author : cuiluping +//Email : luping.cui@rivai-ic.com.cn +//Date : 2019-08-24 +//Description : with reset ,with enable D flip flop, +// reset value can be configured +//----------------------------------------------- +//All Rights Reserved by rivai company +//=============================================== + +module std_dffrve +#( + parameter WIDTH = 8 +) +( + input clk, + input rstn, + input [WIDTH-1:0] rst_val, + input en, + input [WIDTH-1:0] d, + output [WIDTH-1:0] q +); + +logic [WIDTH-1:0] dff_q; + +always_ff @(posedge clk or negedge rstn) begin + if(~rstn)begin + dff_q <= rst_val; + end + else if(en)begin + dff_q <= d; + end +end + +assign q = dff_q; + +endmodule diff --git a/rtl/model/simple_dual_one_clock.v b/rtl/model/simple_dual_one_clock.v new file mode 100644 index 0000000..07c0774 --- /dev/null +++ b/rtl/model/simple_dual_one_clock.v @@ -0,0 +1,47 @@ +// Simple Dual-Port Block RAM with One Clock +// reference: https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/Single-Port-Block-RAM-No-Change-Mode-Verilog +// File: simple_dual_one_clock.v + +module simple_dual_one_clock +#( + parameter ADDR_BITS = 4, + parameter DATA_BITS = 8 + // parameter RAM_LATENCY = 1, + // parameter WE_SIZE = 1, + +) +(clk,ena,enb,wea,addra,addrb,dia,dob); + +input clk,ena,enb,wea; + +input [ADDR_BITS-1:0] addra,addrb; + +input [DATA_BITS-1:0] dia; + +output [DATA_BITS-1:0] dob; + +reg [DATA_BITS-1:0] ram [(1'b1< 1 ? $clog2(OUTPUT_VC_NUM) : 1, + +parameter SA_GLOBAL_INPUT_NUM = 4, +parameter SA_GLOBAL_INPUT_NUM_IDX_W = SA_GLOBAL_INPUT_NUM > 1 ? $clog2(SA_GLOBAL_INPUT_NUM) : 1, + +parameter OUTPUT_TO_N = 0, +parameter OUTPUT_TO_S = 0, +parameter OUTPUT_TO_E = 0, +parameter OUTPUT_TO_W = 0, +parameter OUTPUT_TO_L = 0 +) +( +// input from global sa +input logic sa_global_vld_i, +// input logic [SA_GLOBAL_INPUT_NUM_IDX_W-1:0] sa_global_inport_id_i, +`ifdef COMMON_QOS_EXTRA_RT_VC +input logic [QoS_Value_Width-1:0] sa_global_qos_value_i, +`endif +input logic [SA_GLOBAL_INPUT_NUM-1:0] sa_global_inport_id_oh_i, + +// input from look-ahead-routing +input io_port_t [SA_GLOBAL_INPUT_NUM-1:0] look_ahead_routing_i, // NOTICE: not all routing results, only the ones connected to this global sa + +// input from vc selection +input vc_select_vld_t [OUTPUT_VC_NUM-1:0] vc_select_vld_i, +input vc_select_vc_id_t [OUTPUT_VC_NUM-1:0] vc_select_vc_id_i, + +// output +output logic vc_assignment_vld_o, +output logic [VC_ID_NUM_MAX_W-1:0] vc_assignment_vc_id_o, +output io_port_t look_ahead_routing_sel_o +); + +genvar i; + +// select the look-ahead routing of the flit which wins the global sa +io_port_t look_ahead_routing_sel; +// assign look_ahead_routing_sel = look_ahead_routing_i[sa_global_inport_id_i]; +onehot_mux +#( + .SOURCE_COUNT(SA_GLOBAL_INPUT_NUM ), + .DATA_WIDTH ($bits(io_port_t) ) +) +onehot_mux_look_ahead_routing_sel_u ( + .sel_i (sa_global_inport_id_oh_i ), + .data_i (look_ahead_routing_i ), + .data_o (look_ahead_routing_sel) +); +assign look_ahead_routing_sel_o = look_ahead_routing_sel; + + +logic sa_global_sel_rt_vc_flit_en; +logic [OUTPUT_VC_NUM-QOS_VC_NUM_PER_INPUT-1:0] vc_select_vld; // if local port as outport, doesn't take rt vc into consideration +logic [OUTPUT_VC_NUM-QOS_VC_NUM_PER_INPUT-1:0][VC_ID_NUM_MAX_W-1:0] vc_select_vc_id; // if local port as outport, doesn't take rt vc into consideration + +`ifdef COMMON_QOS_EXTRA_RT_VC +assign sa_global_sel_rt_vc_flit_en = &sa_global_qos_value_i; // rt vc has highest QoS value +`else +assign sa_global_sel_rt_vc_flit_en = '0; +`endif + + +// vc_select_vld +// for vc_select_vld_i, the [0:QOS_VC_NUM_PER_INPUT-1] is rt vc, [QOS_VC_NUM_PER_INPUT:OUTPUT_VC_NUM-1] is common vc +generate + for(i = 0; i < OUTPUT_VC_NUM-QOS_VC_NUM_PER_INPUT; i++) begin: gen_vc_select_vld + assign vc_select_vld[i] = sa_global_sel_rt_vc_flit_en ? vc_select_vld_i[0].rt_vld : vc_select_vld_i[i+QOS_VC_NUM_PER_INPUT].common_vld; + end +endgenerate + +// vc_select_vc_id +generate + for(i = 0; i < OUTPUT_VC_NUM-QOS_VC_NUM_PER_INPUT; i++) begin: gen_vc_select_vc_id + assign vc_select_vc_id[i] = sa_global_sel_rt_vc_flit_en ? vc_select_vc_id_i[0].rt_vc_id : vc_select_vc_id_i[i+QOS_VC_NUM_PER_INPUT].common_vc_id; + end +endgenerate + +// vc assignment for different output ports +// to N vc: +// vc0 +generate + + if(OUTPUT_TO_N) begin: gen_output_to_n // output to N, next hop input is S + always_comb begin + vc_assignment_vld_o = 1'b0; + vc_assignment_vc_id_o = '0; + unique case(look_ahead_routing_sel) + N: begin + vc_assignment_vld_o = vc_select_vld[0]; + vc_assignment_vc_id_o = vc_select_vc_id[0]; + end + L0: begin + vc_assignment_vld_o = vc_select_vld[1]; + vc_assignment_vc_id_o = vc_select_vc_id[1]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + vc_assignment_vld_o = vc_select_vld[2]; + vc_assignment_vc_id_o = vc_select_vc_id[2]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + vc_assignment_vld_o = vc_select_vld[3]; + vc_assignment_vc_id_o = vc_select_vc_id[3]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + vc_assignment_vld_o = vc_select_vld[4]; + vc_assignment_vc_id_o = vc_select_vc_id[4]; + end +`endif + default: begin + end + endcase + end + end + + if(OUTPUT_TO_S) begin: gen_output_to_s // output to S, next hop input is N + always_comb begin + vc_assignment_vld_o = 1'b0; + vc_assignment_vc_id_o = '0; + unique case(look_ahead_routing_sel) + S: begin + vc_assignment_vld_o = vc_select_vld[0]; + vc_assignment_vc_id_o = vc_select_vc_id[0]; + end + L0: begin + vc_assignment_vld_o = vc_select_vld[1]; + vc_assignment_vc_id_o = vc_select_vc_id[1]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + vc_assignment_vld_o = vc_select_vld[2]; + vc_assignment_vc_id_o = vc_select_vc_id[2]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + vc_assignment_vld_o = vc_select_vld[3]; + vc_assignment_vc_id_o = vc_select_vc_id[3]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + vc_assignment_vld_o = vc_select_vld[4]; + vc_assignment_vc_id_o = vc_select_vc_id[4]; + end +`endif + default: begin + end + endcase + end + end + + if(OUTPUT_TO_E) begin: gen_output_to_e // output to E, next hop input is W + always_comb begin + vc_assignment_vld_o = 1'b0; + vc_assignment_vc_id_o = '0; + unique case(look_ahead_routing_sel) + N: begin + vc_assignment_vld_o = vc_select_vld[0]; + vc_assignment_vc_id_o = vc_select_vc_id[0]; + end + S: begin + vc_assignment_vld_o = vc_select_vld[1]; + vc_assignment_vc_id_o = vc_select_vc_id[1]; + end + E: begin + vc_assignment_vld_o = vc_select_vld[2]; + vc_assignment_vc_id_o = vc_select_vc_id[2]; + end + L0: begin + vc_assignment_vld_o = vc_select_vld[3]; + vc_assignment_vc_id_o = vc_select_vc_id[3]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + vc_assignment_vld_o = vc_select_vld[4]; + vc_assignment_vc_id_o = vc_select_vc_id[4]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + vc_assignment_vld_o = vc_select_vld[5]; + vc_assignment_vc_id_o = vc_select_vc_id[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + vc_assignment_vld_o = vc_select_vld[6]; + vc_assignment_vc_id_o = vc_select_vc_id[6]; + end +`endif + default: begin + end + endcase + end + end + + if(OUTPUT_TO_W) begin: gen_output_to_w // output to W, next hop input is E + always_comb begin + vc_assignment_vld_o = 1'b0; + vc_assignment_vc_id_o = '0; + unique case(look_ahead_routing_sel) + N: begin + vc_assignment_vld_o = vc_select_vld[0]; + vc_assignment_vc_id_o = vc_select_vc_id[0]; + end + S: begin + vc_assignment_vld_o = vc_select_vld[1]; + vc_assignment_vc_id_o = vc_select_vc_id[1]; + end + W: begin + vc_assignment_vld_o = vc_select_vld[2]; + vc_assignment_vc_id_o = vc_select_vc_id[2]; + end + L0: begin + vc_assignment_vld_o = vc_select_vld[3]; + vc_assignment_vc_id_o = vc_select_vc_id[3]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + vc_assignment_vld_o = vc_select_vld[4]; + vc_assignment_vc_id_o = vc_select_vc_id[4]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + vc_assignment_vld_o = vc_select_vld[5]; + vc_assignment_vc_id_o = vc_select_vc_id[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + vc_assignment_vld_o = vc_select_vld[6]; + vc_assignment_vc_id_o = vc_select_vc_id[6]; + end +`endif + default: begin + end + endcase + end + end + + if(OUTPUT_TO_L) begin: gen_output_to_l // output to L, no next hop + always_comb begin + vc_assignment_vld_o = 1'b0; + vc_assignment_vc_id_o = '0; + unique case(look_ahead_routing_sel) // assume all local port have 1 vc + default: begin + vc_assignment_vld_o = vc_select_vld_i [0].common_vld; + vc_assignment_vc_id_o = vc_select_vc_id_i[0].common_vc_id; + end + endcase + end + end + +endgenerate + + +endmodule diff --git a/rtl/output_port_vc_credit_counter.sv b/rtl/output_port_vc_credit_counter.sv new file mode 100644 index 0000000..4147cad --- /dev/null +++ b/rtl/output_port_vc_credit_counter.sv @@ -0,0 +1,97 @@ +// vc credit counter per out port port +module output_port_vc_credit_counter + import rvh_noc_pkg::*; +#( + parameter VC_NUM = 4, + parameter VC_NUM_IDX_W = VC_NUM > 1 ? $clog2(VC_NUM) : 1, + parameter VC_DEPTH = 1, + parameter VC_DEPTH_COUNTER_W = $clog2(VC_DEPTH+1) +) +( + // input new free vc credit + input logic free_vc_credit_vld_i, + input logic [VC_NUM_IDX_W-1:0] free_vc_credit_vc_id_i, + + // input new consume vc credit + input logic consume_vc_credit_vld_i, + input logic [VC_NUM_IDX_W-1:0] consume_vc_credit_vc_id_i, + + // output to vc selection + output logic [VC_NUM-1:0][VC_DEPTH_COUNTER_W-1:0] vc_credit_counter_o, + + input logic clk, + input logic rstn + +); +genvar i; + +// credit counter +logic [VC_NUM-1:0][VC_DEPTH_COUNTER_W-1:0] vc_credit_counter_d, vc_credit_counter_q; +logic [VC_NUM-1:0][VC_DEPTH_COUNTER_W-1:0] vc_credit_counter_q_plus1, vc_credit_counter_q_minus1; +logic [VC_NUM-1:0] vc_credit_counter_ena; + +// credit counter nxt +logic [VC_NUM-1:0] free_vc_credit_vc_id_hit; +logic [VC_NUM-1:0] consume_vc_credit_vc_id_hit; +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_credit_vc_id_hit + assign free_vc_credit_vc_id_hit [i] = free_vc_credit_vld_i & (free_vc_credit_vc_id_i == i[VC_NUM_IDX_W-1:0]); + assign consume_vc_credit_vc_id_hit[i] = consume_vc_credit_vld_i & (consume_vc_credit_vc_id_i == i[VC_NUM_IDX_W-1:0]); + end +endgenerate + +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_credit_counter_q_plus1 + assign vc_credit_counter_q_plus1[i] = vc_credit_counter_q[i] + 1; + end +endgenerate + +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_credit_counter_q_minus1 + assign vc_credit_counter_q_minus1[i] = vc_credit_counter_q[i] - 1; + end +endgenerate + +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_credit_counter_d + always_comb begin + vc_credit_counter_d [i] = vc_credit_counter_q[i]; + vc_credit_counter_ena [i] = 1'b0; + if(free_vc_credit_vc_id_hit[i] & ~consume_vc_credit_vc_id_hit[i]) begin + vc_credit_counter_d [i] = vc_credit_counter_q_plus1[i]; + vc_credit_counter_ena [i] = 1'b1; + end else if(~free_vc_credit_vc_id_hit[i] & consume_vc_credit_vc_id_hit[i]) begin + vc_credit_counter_d [i] = vc_credit_counter_q_minus1[i]; + vc_credit_counter_ena [i] = 1'b1; + end + end + end +endgenerate + +// dff vc_credit_counter_q +generate + for(i = 0; i < VC_NUM; i++) begin: gen_vc_credit_counter_q + std_dffrve + #(.WIDTH(VC_DEPTH_COUNTER_W)) + U_DAT_VC_CREDIT_CONTER_REG + ( + .clk(clk), + .rstn(rstn), + .rst_val(VC_DEPTH[VC_DEPTH_COUNTER_W-1:0]), + .en(vc_credit_counter_ena[i]), + .d(vc_credit_counter_d[i]), + .q(vc_credit_counter_q[i]) + ); + +`ifndef SYNTHESIS + assert property(@(posedge clk)disable iff(~rstn) ((vc_credit_counter_q[i]) <= VC_DEPTH)) + else $fatal("output_port_vc_credit_counter: vc credit counter overflow"); +`endif + end +endgenerate + +// output to vc selection +assign vc_credit_counter_o = vc_credit_counter_q; + + +endmodule diff --git a/rtl/output_port_vc_selection.sv b/rtl/output_port_vc_selection.sv new file mode 100644 index 0000000..6ec2ecd --- /dev/null +++ b/rtl/output_port_vc_selection.sv @@ -0,0 +1,108 @@ +// vc selection per output port to pre-allocate every possible next hop output port for vc assignment +module output_port_vc_selection +import rvh_noc_pkg::*; +#( +parameter OUTPUT_VC_NUM = 4, +parameter OUTPUT_VC_NUM_IDX_W = OUTPUT_VC_NUM > 1 ? $clog2(OUTPUT_VC_NUM) : 1, +parameter OUTPUT_VC_DEPTH = 1, +parameter OUTPUT_VC_DEPTH_IDX_W = $clog2(OUTPUT_VC_DEPTH + 1), + +parameter OUTPUT_TO_L = 0 +) +( +// input from per output port vc credit counter +input logic [OUTPUT_VC_NUM-1:0][OUTPUT_VC_DEPTH_IDX_W-1:0] vc_credit_counter_i, + +// output to vc assignment +output vc_select_vld_t [OUTPUT_VC_NUM-1:0] vc_select_vld_o, +output vc_select_vc_id_t [OUTPUT_VC_NUM-1:0] vc_select_vc_id_o + +); + +genvar i; + +logic [OUTPUT_VC_NUM-1:0] vc_credit_counter_not_empty; + +generate + for(i = 0; i < OUTPUT_VC_NUM; i++) begin + assign vc_credit_counter_not_empty[i] = |(vc_credit_counter_i[i]); + end +endgenerate + + +generate + if(!OUTPUT_TO_L) begin + always_comb begin: comb_common_vc_id + for(int i = QOS_VC_NUM_PER_INPUT; i < OUTPUT_VC_NUM; i++) begin + vc_select_vld_o [i].common_vld = '0; + vc_select_vc_id_o[i].common_vc_id = '0; + vc_select_vld_o [i].rt_vld = '0; + vc_select_vc_id_o[i].rt_vc_id = '0; + if(vc_credit_counter_not_empty[i]) begin // the priority output port vc has free credit, assign it to the flit + vc_select_vld_o [i].common_vld = 1'b1; + vc_select_vc_id_o [i].common_vc_id = i[VC_ID_NUM_MAX_W-1:0]; + end else begin // the priority output port vc has no free credit, try other vc + for(int j = QOS_VC_NUM_PER_INPUT; j < OUTPUT_VC_NUM; j++) begin + if(j != i) begin + if(vc_credit_counter_not_empty[j]) begin + vc_select_vld_o [i].common_vld = 1'b1; + vc_select_vc_id_o [i].common_vc_id = j[VC_ID_NUM_MAX_W-1:0]; + end + end + end + end + end + end + + `ifdef COMMON_QOS_EXTRA_RT_VC + always_comb begin: comb_rt_vc_id + for(int i = 0; i < QOS_VC_NUM_PER_INPUT; i++) begin + vc_select_vld_o [i].common_vld = '0; + vc_select_vc_id_o[i].common_vc_id = '0; + vc_select_vld_o [i].rt_vld = '0; + vc_select_vc_id_o[i].rt_vc_id = '0; + if(vc_credit_counter_not_empty[i]) begin // the priority output port vc has free credit, assign it to the flit + vc_select_vld_o [i].rt_vld = 1'b1; + vc_select_vc_id_o [i].rt_vc_id = i[VC_ID_NUM_MAX_W-1:0]; + end else begin // the priority output port vc has no free credit, try other vc + for(int j = 0; j < QOS_VC_NUM_PER_INPUT; j++) begin + if(j != i) begin + if(vc_credit_counter_not_empty[j]) begin + vc_select_vld_o [i].rt_vld = 1'b1; + vc_select_vc_id_o [i].rt_vc_id = j[VC_ID_NUM_MAX_W-1:0]; + end + end + end + end + end + end + `endif + + end else begin // it is output to L, just common vc + + always_comb begin + for(int i = 0; i < OUTPUT_VC_NUM; i++) begin + vc_select_vld_o [i].common_vld = '0; + vc_select_vc_id_o[i].common_vc_id = '0; + vc_select_vld_o [i].rt_vld = '0; + vc_select_vc_id_o[i].rt_vc_id = '0; + if(vc_credit_counter_not_empty[i]) begin // the priority output port vc has free credit, assign it to the flit + vc_select_vld_o [i].common_vld = 1'b1; + vc_select_vc_id_o [i].common_vc_id = i[VC_ID_NUM_MAX_W-1:0]; + end else begin // the priority output port vc has no free credit, try other vc + for(int j = 0; j < OUTPUT_VC_NUM; j++) begin + if(j != i) begin + if(vc_credit_counter_not_empty[j]) begin + vc_select_vld_o [i].common_vld = 1'b1; + vc_select_vc_id_o [i].common_vc_id = j[VC_ID_NUM_MAX_W-1:0]; + end + end + end + end + end + end + + end +endgenerate + +endmodule diff --git a/rtl/performance_monitor.sv b/rtl/performance_monitor.sv new file mode 100644 index 0000000..d161b33 --- /dev/null +++ b/rtl/performance_monitor.sv @@ -0,0 +1,175 @@ +// this module records many perforance counters +module performance_monitor +import rvh_noc_pkg::*; +#( + parameter INPUT_PORT_NUM = 5, + parameter OUTPUT_PORT_NUM = 5, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_L = 4, + + parameter VC_DEPTH_INPUT_N = 2, + parameter VC_DEPTH_INPUT_S = 2, + parameter VC_DEPTH_INPUT_E = 2, + parameter VC_DEPTH_INPUT_W = 2, + parameter VC_DEPTH_INPUT_L = 2, + parameter VC_DEPTH_INPUT_N_COUNTER_W = $clog2(VC_DEPTH_INPUT_N + 1), + parameter VC_DEPTH_INPUT_S_COUNTER_W = $clog2(VC_DEPTH_INPUT_S + 1), + parameter VC_DEPTH_INPUT_E_COUNTER_W = $clog2(VC_DEPTH_INPUT_E + 1), + parameter VC_DEPTH_INPUT_W_COUNTER_W = $clog2(VC_DEPTH_INPUT_W + 1), + parameter VC_DEPTH_INPUT_L_COUNTER_W = $clog2(VC_DEPTH_INPUT_L + 1) + +) +( + // 1. sa global util + // input from sa local + input logic [INPUT_PORT_NUM-1:0] sa_local_vld_i, + // input from sa global + input logic [INPUT_PORT_NUM-1:0] sa_global_inport_read_vld_i, + + // 2. outport credit util + input logic [VC_NUM_INPUT_N-1:0][VC_DEPTH_INPUT_N_COUNTER_W-1:0] vc_credit_counter_toN_i, + input logic [VC_NUM_INPUT_S-1:0][VC_DEPTH_INPUT_S_COUNTER_W-1:0] vc_credit_counter_toS_i, + input logic [VC_NUM_INPUT_E-1:0][VC_DEPTH_INPUT_E_COUNTER_W-1:0] vc_credit_counter_toE_i, + input logic [VC_NUM_INPUT_W-1:0][VC_DEPTH_INPUT_W_COUNTER_W-1:0] vc_credit_counter_toW_i, +`ifdef HAVE_LOCAL_PORT + input logic [LOCAL_PORT_NUM-1:0][VC_NUM_INPUT_L-1:0][VC_DEPTH_INPUT_L_COUNTER_W-1:0] vc_credit_counter_toL_i, +`endif + + // router addr + input logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i, + input logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i, + + input logic clk, + input logic rstn +); + +genvar i; + +// global sa efficiency +logic [INPUT_PORT_NUM-1:0][64-1:0] sa_local_vld_counter_d, sa_local_vld_counter_q; +logic [INPUT_PORT_NUM-1:0] sa_local_vld_counter_ena; +logic [INPUT_PORT_NUM-1:0][64-1:0] sa_global_inport_read_vld_counter_d, sa_global_inport_read_vld_counter_q; +logic [INPUT_PORT_NUM-1:0] sa_global_inport_read_vld_counter_ena; + +always_comb begin + sa_local_vld_counter_d = sa_local_vld_counter_q; + sa_local_vld_counter_ena = '0; + sa_global_inport_read_vld_counter_d = sa_global_inport_read_vld_counter_q; + sa_global_inport_read_vld_counter_ena = '0; + + for(int i = 0; i < INPUT_PORT_NUM; i++) begin + if(sa_local_vld_i[i]) begin + sa_local_vld_counter_d[i] = sa_local_vld_counter_d[i] + 1; + sa_local_vld_counter_ena[i] = 1'b1; + end + if(sa_global_inport_read_vld_i[i]) begin + sa_global_inport_read_vld_counter_d [i] = sa_global_inport_read_vld_counter_d[i] + 1; + sa_global_inport_read_vld_counter_ena [i] = 1'b1; + end + end +end + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin + std_dffre + #(.WIDTH(64)) + U_DAT_SA_LOCAL_VLD_COUNTER + ( + .clk(clk), + .rstn(rstn), + .en(sa_local_vld_counter_ena[i]), + .d(sa_local_vld_counter_d[i]), + .q(sa_local_vld_counter_q[i]) + ); + + std_dffre + #(.WIDTH(64)) + U_DAT_SA_GLOBAL_INPORT_READ_VLD_COUNTER + ( + .clk(clk), + .rstn(rstn), + .en(sa_global_inport_read_vld_counter_ena[i]), + .d(sa_global_inport_read_vld_counter_d[i]), + .q(sa_global_inport_read_vld_counter_q[i]) + ); + end +endgenerate + +`ifdef V_ROUTER_PM_PRINT_EN +// display + // 1. sa global util +real sa_local_vld_counter [INPUT_PORT_NUM-1:0]; +real sa_global_inport_read_vld_counter[INPUT_PORT_NUM-1:0]; +real sa_local_vld_counter_all; +real sa_global_inport_read_vld_counter_all; + +always_ff @(posedge clk) begin + if(~rstn) begin + + end else begin + sa_local_vld_counter_all = 0; + sa_global_inport_read_vld_counter_all = 0; + for(int i = 0; i < INPUT_PORT_NUM; i++) begin + sa_local_vld_counter[i] = sa_local_vld_counter_q[i]; + sa_global_inport_read_vld_counter[i] = sa_global_inport_read_vld_counter_q[i]; + sa_local_vld_counter_all = sa_local_vld_counter_all + sa_local_vld_counter_q[i]; + sa_global_inport_read_vld_counter_all = sa_global_inport_read_vld_counter_all + sa_global_inport_read_vld_counter_q[i]; + + $display("[%16d] info: (router %d,%d inport %2d) pm global sa efficiency (sa_global/sa_local): (%16d/%16d) = %f", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, + sa_global_inport_read_vld_counter[i], sa_local_vld_counter[i], + sa_global_inport_read_vld_counter[i]/sa_local_vld_counter[i]); + end + $display("[%16d] info: pm (inportall) global sa efficiency (sa_global/sa_local): (%16d/%16d) = %f", + $time(), + sa_global_inport_read_vld_counter_all, sa_local_vld_counter_all, + sa_global_inport_read_vld_counter_all/sa_local_vld_counter_all); + + end +end + + // 2. outport credit util + always_ff @(posedge clk) begin + if(~rstn) begin + + end else begin + for(int i = 0; i < VC_NUM_INPUT_N; i++) begin + $display("[%16d] info: (router %d,%d outport N vc id %d) pm credit count: %d", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, + vc_credit_counter_toN_i[i]); + end + for(int i = 0; i < VC_NUM_INPUT_S; i++) begin + $display("[%16d] info: (router %d,%d outport S vc id %d) pm credit count: %d", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, + vc_credit_counter_toS_i[i]); + end + for(int i = 0; i < VC_NUM_INPUT_E; i++) begin + $display("[%16d] info: (router %d,%d outport E vc id %d) pm credit count: %d", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, + vc_credit_counter_toE_i[i]); + end + for(int i = 0; i < VC_NUM_INPUT_W; i++) begin + $display("[%16d] info: (router %d,%d outport W vc id %d) pm credit count: %d", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, + vc_credit_counter_toW_i[i]); + end +`ifdef HAVE_LOCAL_PORT + for(int i = 0; i < LOCAL_PORT_NUM; i++) begin + for(int j = 0; j < VC_NUM_INPUT_L; j++) begin + $display("[%16d] info: (router %d,%d outport L%1d vc id %d) pm credit count: %d", + $time(), node_id_x_ths_hop_i, node_id_y_ths_hop_i, i, j, + vc_credit_counter_toL_i[i][j]); + end + end +`endif + end + end + + `endif + +endmodule diff --git a/rtl/priority_req_select.sv b/rtl/priority_req_select.sv new file mode 100644 index 0000000..a9d7d25 --- /dev/null +++ b/rtl/priority_req_select.sv @@ -0,0 +1,43 @@ +// select req(s) with the highest priority from the req input vector +module priority_req_select + import rvh_noc_pkg::*; +#( + parameter INPUT_NUM = 4, + parameter INPUT_NUM_IDX_W = INPUT_NUM > 1 ? $clog2(INPUT_NUM) : 1, + parameter INPUT_PRIORITY_W = 4 +) +( + input logic [INPUT_NUM-1:0] req_vld_i, + input logic [INPUT_NUM-1:0][INPUT_PRIORITY_W-1:0] req_priority_i, + + output logic [INPUT_NUM-1:0] req_vld_o +); +genvar i,j; + +// x >= y -> 1 else 0 +// req_vld_i pri 3 9 2 8 & req_vld_o +// 1 3 1 1 1 0 0 0 +// 0 9 0 0 0 0 0 0 +// 1 2 0 1 1 0 0 0 +// 1 8 1 1 1 1 1 1 + +logic [INPUT_NUM-1:0][INPUT_NUM-1:0] priority_compare_vector; +generate + for(i = 0; i < INPUT_NUM; i++) begin: gen_priority_compare_vector_i + for(j = 0; j < INPUT_NUM; j++) begin: gen_priority_compare_vector_j + if (i == j) begin: gen_diagonal + assign priority_compare_vector[i][j] = req_vld_i[i]; + end else begin: gen_others + assign priority_compare_vector[i][j] = ~req_vld_i[j] | (req_priority_i[i] >= req_priority_i[j]); + end + end + end +endgenerate + +generate + for(i = 0; i < INPUT_NUM; i++) begin: gen_req_vld_o + assign req_vld_o[i] = &(priority_compare_vector[i]); + end +endgenerate + +endmodule diff --git a/rtl/ruby/ut_lib.sv b/rtl/ruby/ut_lib.sv new file mode 100755 index 0000000..3ef24ee --- /dev/null +++ b/rtl/ruby/ut_lib.sv @@ -0,0 +1,467 @@ +`define LFSR_HIGH_16BIT_ENABLE + +module LFSR #(parameter NUM_BITS) +( + input i_Clk, + input i_Enable, + + //Optional Seed Value + input i_Seed_DV, + input [NUM_BITS-1:0] i_Seed_Data, + output[NUM_BITS-1:0] o_LFSR_Data, + output o_LFSR_Done +); + + reg [NUM_BITS:1] r_LFSR = 0; + reg r_XNOR; + + always @(posedge i_Clk or negedge i_Enable) + begin + if(~i_Enable) begin + r_LFSR <= i_Seed_Data; + end else if (i_Seed_DV == 1'b1) begin + r_LFSR <= i_Seed_Data; + end else begin + r_LFSR <= {r_LFSR[NUM_BITS-1:1],r_XNOR}; + end + end + + // Linear Feedback Shift Register Taps refer to https://docs.xilinx.com/v/u/en-US/xapp052 + // use same or becaure the default seed can be '0, and should not be '1 + always @(*) + begin + case (NUM_BITS) + 2: begin + r_XNOR = r_LFSR[2] ^ ~r_LFSR[1]; + end + 3: begin + r_XNOR = r_LFSR[3] ^ ~r_LFSR[2]; + end + 4: begin + r_XNOR = r_LFSR[4] ^ ~r_LFSR[3]; + end + 5: begin + r_XNOR = r_LFSR[5] ^ ~r_LFSR[3]; + end + 6: begin + r_XNOR = r_LFSR[6] ^ ~r_LFSR[5]; + end + 7: begin + r_XNOR = r_LFSR[7] ^ ~r_LFSR[6]; + end + 8: begin + r_XNOR = r_LFSR[8] ^ ~r_LFSR[6] ^ ~r_LFSR[5] ^ ~r_LFSR[4]; + end + 9: begin + r_XNOR = r_LFSR[9] ^ ~r_LFSR[5]; + end + 10: begin + r_XNOR = r_LFSR[10] ^ ~r_LFSR[7]; + end + 11: begin + r_XNOR = r_LFSR[11] ^ ~r_LFSR[9]; + end + 12: begin + r_XNOR = r_LFSR[12] ^ ~r_LFSR[6] ^ ~r_LFSR[4] ^ ~r_LFSR[1]; + end + 13: begin + r_XNOR = r_LFSR[13] ^ ~r_LFSR[4] ^ ~r_LFSR[3] ^ ~r_LFSR[1]; + end + 14: begin + r_XNOR = r_LFSR[14] ^ ~r_LFSR[5] ^ ~r_LFSR[3] ^ ~r_LFSR[1]; + end + 15: begin + r_XNOR = r_LFSR[15] ^ ~r_LFSR[14]; + end + 16: begin + r_XNOR = r_LFSR[16] ^ ~r_LFSR[15] ^ ~r_LFSR[13] ^ ~r_LFSR[4]; + end +`ifdef LFSR_HIGH_16BIT_ENABLE + 17: begin + r_XNOR = r_LFSR[17] ^ ~r_LFSR[14]; + end + 18: begin + r_XNOR = r_LFSR[18] ^ ~r_LFSR[11]; + end + 19: begin + r_XNOR = r_LFSR[19] ^ ~r_LFSR[6] ^ ~r_LFSR[2] ^ ~r_LFSR[1]; + end + 20: begin + r_XNOR = r_LFSR[20] ^ ~r_LFSR[17]; + end + 21: begin + r_XNOR = r_LFSR[21] ^ ~r_LFSR[19]; + end + 22: begin + r_XNOR = r_LFSR[22] ^ ~r_LFSR[21]; + end + 23: begin + r_XNOR = r_LFSR[23] ^ ~r_LFSR[18]; + end + 24: begin + r_XNOR = r_LFSR[24] ^ ~r_LFSR[23] ^ ~r_LFSR[22] ^ ~r_LFSR[17]; + end + 25: begin + r_XNOR = r_LFSR[25] ^ ~r_LFSR[22]; + end + 26: begin + r_XNOR = r_LFSR[26] ^ ~r_LFSR[6] ^ ~r_LFSR[2] ^ ~r_LFSR[1]; + end + 27: begin + r_XNOR = r_LFSR[27] ^ ~r_LFSR[5] ^ ~r_LFSR[2] ^ ~r_LFSR[1]; + end + 28: begin + r_XNOR = r_LFSR[28] ^ ~r_LFSR[25]; + end + 29: begin + r_XNOR = r_LFSR[29] ^ ~r_LFSR[27]; + end + 30: begin + r_XNOR = r_LFSR[30] ^ ~r_LFSR[6] ^ ~r_LFSR[4] ^ ~r_LFSR[1]; + end + 31: begin + r_XNOR = r_LFSR[31] ^ ~r_LFSR[28]; + end + 32: begin + r_XNOR = r_LFSR[32] ^ ~r_LFSR[22] ^ ~r_LFSR[2] ^ ~r_LFSR[1]; + end + 64: begin + r_XNOR = r_LFSR[64] ^ ~r_LFSR[63] ^ ~r_LFSR[61] ^ ~r_LFSR[60]; + end + 80: begin + r_XNOR = r_LFSR[80] ^ ~r_LFSR[79] ^ ~r_LFSR[43] ^ ~r_LFSR[42]; + end + 128: begin + r_XNOR = r_LFSR[128] ^ ~r_LFSR[126] ^ ~r_LFSR[101] ^ ~r_LFSR[99]; + end + 168: begin + r_XNOR = r_LFSR[168] ^ ~r_LFSR[166] ^ ~r_LFSR[153] ^ ~r_LFSR[151]; + end +`endif + default: begin + r_XNOR = '0; + end + + endcase + end + + + assign o_LFSR_Data = r_LFSR[NUM_BITS:1]; + assign o_LFSR_Done = (r_LFSR[NUM_BITS:1] == i_Seed_Data) ? 1'b1 : 1'b0; +endmodule +module id_pool_2w1r + #( parameter depth=8, + parameter width=32, + parameter ptr_sz=$clog2(depth) + ) + ( + input logic clk, + input logic rst_n, + input logic [1:0] c_srdy, + output logic [1:0] c_drdy, + input logic [1:0][width-1:0] c_data, + output logic p_srdy, + input logic p_drdy, + output logic [width-1:0] p_data, + output logic [ptr_sz:0] usage + ); + + localparam depth_sub_1 = depth-1; + localparam depth_sub_2 = depth-2; + + logic [ptr_sz-1:0] wr_addr_d; + logic [ptr_sz-1:0] rd_addr_d; + + logic [ptr_sz-1:0] wr_addr_q; + logic [ptr_sz-1:0] rd_addr_q; + + logic [depth-1:0][width-1:0] array_d; + logic [depth-1:0][width-1:0] array_q; + + logic [ptr_sz:0] occupy_cnt_d; + logic [ptr_sz:0] occupy_cnt_q; + + logic full; + logic empty; + + logic wr_2_en; + logic wr_2_vld; + logic wr_en; + logic rd_en; + logic [ptr_sz-1:0] wr_addr_p1; + + assign c_drdy = wr_2_vld ? 2'b11 : (occupy_cnt_q == depth_sub_1) ? ((c_srdy == 2'b10) ? 2'b10 : 2'b01) : '0; + assign p_srdy = ~empty; + assign p_data = array_q[rd_addr_q]; + + assign empty = (occupy_cnt_q == '0); + assign full = (occupy_cnt_q == depth[ptr_sz:0]); + + assign rd_en = p_drdy & ~empty; + assign wr_en = (c_srdy[0] | c_srdy[1]) & ~full; + assign wr_2_vld = (occupy_cnt_q < depth_sub_1); + assign wr_2_en = (c_srdy == 2'b11) & wr_2_vld; + + assign rd_addr_d = rd_en ? ((rd_addr_q == depth_sub_1) ? '0 : rd_addr_q+1) : rd_addr_q; + assign wr_addr_p1 = (wr_addr_q == depth_sub_1) ? '0 : wr_addr_q+1; + assign wr_addr_d = wr_2_en ? ((wr_addr_q == depth_sub_1) ? ptr_sz'('d1) : (wr_addr_q == depth_sub_2) ? '0 : wr_addr_q+2) : wr_en ? wr_addr_p1 : wr_addr_q; + always_comb begin + occupy_cnt_d = occupy_cnt_q; + case ({wr_2_en,wr_en,rd_en}) + 3'b110: begin + occupy_cnt_d = occupy_cnt_q+{{(ptr_sz-1){1'b0}},2'd2}; + end + 3'b111: begin + occupy_cnt_d = occupy_cnt_q+{{(ptr_sz){1'b0}},1'b1}; + end + 3'b010: begin + occupy_cnt_d = occupy_cnt_q+{{(ptr_sz){1'b0}},1'b1}; + end + 3'b001: begin + occupy_cnt_d = occupy_cnt_q-{{(ptr_sz){1'b0}},1'b1}; + end + endcase + end + +std_dffr #(ptr_sz) FF_WR_ADDR (.clk(clk) ,.rstn(rst_n),.d(wr_addr_d),.q(wr_addr_q)); +std_dffr #(ptr_sz) FF_RD_ADDR (.clk(clk) ,.rstn(rst_n),.d(rd_addr_d),.q(rd_addr_q)); +std_dffrve #(ptr_sz+1) FF_OCCUPY_CNT (.clk(clk),.rstn(rst_n),.rst_val(depth),.en(1'b1),.d(occupy_cnt_d),.q(occupy_cnt_q)); + +generate +for (genvar ii=0; ii rff_last_grt_id) begin +// vld_with_priority[i - rff_last_grt_id - 1'b1] = vld[i]; +// end else begin +// vld_with_priority[N_INPUT - rff_last_grt_id - 1'b1 + i] = vld[i]; +// end +// end +// end +// end + +// //grt_with_priority +// always_comb begin +// grt_with_priority = {N_INPUT{1'b0}}: +// if (|vld_with_priority) begin +// for (int i = 0; i < N_INPUT; i++) begin +// if (vld_with_priority[i] == 1'b1) begin +// grt_with_priority[i] = 1'b1; +// break; +// end +// end +// end +// end + +// // next_grt +// always_comb begin +// if(rff_last_grt_id == N_INPUT_BITS'(N_INPUT-1)) begin +// next_grt = grt_with_priority; +// end else begin +// for(int i = 0; i < N_INPUT;i++) begin +// if(i > rff_last_grt_id) begin +// next_grt[i] = grt_with_priority[i - rff_last_grt_id - 1'b1]; +// end else begin +// next_grt[i] = grt_with_priority[N_INPUT - rff_last_grt_id - 1'b1 + i]; +// end +// end +// end +// end + +// // Grant Update +// always_ff @(posedge clk or negedge rstn) begin +// if (!rstn) begin +// rff_grt <= '0; +// end else begin +// if (do_save_grt) begin +// rff_grt <= grt; +// end +// end +// end + +// always_ff @(posedge clk or negedge rstn ) begin +// if (!rstn) begin +// rff_last_grt_id <= N_INPUT_BITS'(N_INPUT-1); +// end else begin +// if ((|grt) & rdy) begin +// rff_last_grt_id <= grt_id; +// end +// end +// end + +// always_ff @(posedge clk or negedge rstn) begin +// if(!rstn) begin +// rff_state <= ST_PASS_THROUGH; +// end else begin +// rff_state <= next_state; +// end +// end + +// always_comb begin +// case (rff_state) +// ST_HOLD: begin +// do_save_grt = '0; +// if (rdy) begin +// next_state = ST_PASS_THROUGH; +// end else begin +// next_state = ST_HOLD; +// end +// end +// default: begin +// if((|grt) & ~rdy) begin +// do_save_grt = '1; +// next_state = ST_HOLD; +// end else begin +// do_save_grt = '0; +// next_state = ST_PASS_THROUGH; +// end +// end +// endcase +// end + +// assign grt = (rff_state == ST_HOLD) ? rff_grt: next_grt; +// end +// endgenerate +//endmodule + diff --git a/rtl/sa_global.sv b/rtl/sa_global.sv new file mode 100644 index 0000000..6b59bb6 --- /dev/null +++ b/rtl/sa_global.sv @@ -0,0 +1,101 @@ +module sa_global + import rvh_noc_pkg::*; +#( + parameter INPUT_NUM = 4, + parameter INPUT_NUM_IDX_W = INPUT_NUM > 1 ? $clog2(INPUT_NUM) : 1 +) +( + // input to allocate + input logic [INPUT_NUM-1:0] sa_local_vld_i, + input logic [INPUT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_i, +`ifdef USE_QOS_VALUE + input logic [INPUT_NUM-1:0][QoS_Value_Width-1:0] sa_local_qos_value_i, +`endif + + // output to VC assignment + output logic sa_global_vld_o, +`ifdef COMMON_QOS_EXTRA_RT_VC + output logic [QoS_Value_Width-1:0] sa_global_qos_value_o, +`endif + output logic [INPUT_NUM-1:0] sa_global_inport_id_oh_o, + output logic [VC_ID_NUM_MAX_W-1:0] sa_global_inport_vc_id_o, + + // input from vc assignment for rr arbiter update + input logic vc_assignment_vld_i, + + input logic clk, + input logic rstn +); + +logic [INPUT_NUM-1:0] sa_global_grt_oh; +logic [INPUT_NUM_IDX_W-1:0] sa_global_grt_idx; + +logic [INPUT_NUM-1:0] sa_local_vld_join_arb; // for qos, if no qos, it is same as sa_local_vld_i + + +`ifdef USE_QOS_VALUE + + priority_req_select + #( + .INPUT_NUM ( INPUT_NUM ), + .INPUT_PRIORITY_W ( QoS_Value_Width ) + ) + sa_local_priority_req_select_u ( + .req_vld_i (sa_local_vld_i ), + .req_priority_i (sa_local_qos_value_i ), + .req_vld_o (sa_local_vld_join_arb ) + ); + +`else + + assign sa_local_vld_join_arb = sa_local_vld_i; + +`endif + + +one_hot_rr_arb #( + .N_INPUT (INPUT_NUM), + + .TIMEOUT_UPDATE_EN (1 ), + .TIMEOUT_UPDATE_CYCLE (10) +) +sa_global_rr_arb_u +( + .req_i (sa_local_vld_join_arb ), + .update_i (vc_assignment_vld_i ), + .grt_o (sa_global_grt_oh ), + .grt_idx_o (sa_global_grt_idx ), + + .rstn (rstn ), + .clk (clk ) +); + +assign sa_global_vld_o = |sa_local_vld_join_arb; +assign sa_global_inport_id_oh_o = sa_global_grt_oh; +// assign sa_global_inport_vc_id_o = sa_local_vc_id_i[sa_global_grt_idx]; + +onehot_mux +#( + .SOURCE_COUNT(INPUT_NUM ), + .DATA_WIDTH (VC_ID_NUM_MAX_W ) +) +onehot_mux_sa_global_inport_vc_id_o_u ( + .sel_i (sa_global_grt_oh ), + .data_i (sa_local_vc_id_i ), + .data_o (sa_global_inport_vc_id_o) +); + +`ifdef COMMON_QOS_EXTRA_RT_VC +onehot_mux +#( + .SOURCE_COUNT(INPUT_NUM ), + .DATA_WIDTH (QoS_Value_Width ) +) +onehot_mux_sa_global_qos_value_o_u ( + .sel_i (sa_global_grt_oh ), + .data_i (sa_local_qos_value_i ), + .data_o (sa_global_qos_value_o) +); +`endif + +endmodule diff --git a/rtl/sa_local.sv b/rtl/sa_local.sv new file mode 100644 index 0000000..1ec3c2e --- /dev/null +++ b/rtl/sa_local.sv @@ -0,0 +1,142 @@ +module sa_local + import rvh_noc_pkg::*; +#( + parameter INPUT_NUM = 4, + parameter INPUT_NUM_IDX_W = INPUT_NUM > 1 ? $clog2(INPUT_NUM) : 1 +) +( + // input to allocate + input logic [INPUT_NUM-1:0] vc_ctrl_head_vld_i, + input flit_dec_t [INPUT_NUM-1:0] vc_ctrl_head_i, + + // output to global allocate + output logic [OUTPUT_PORT_NUMBER-1:0] sa_local_vld_to_sa_global_o, + output logic sa_local_vld_o, + output logic [INPUT_NUM_IDX_W-1:0] sa_local_vc_id_o, + output logic [INPUT_NUM-1:0] sa_local_vc_id_oh_o, +`ifdef USE_QOS_VALUE + output logic [QoS_Value_Width-1:0] sa_local_qos_value_o, +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + output dpram_used_idx_t sa_local_dpram_idx_o, +`endif + + + // input pop flit ctrl fifo (comes from SA stage), use to update rr arbiter pointer + input logic inport_read_enable_sa_stage_i, + + input logic clk, + input logic rstn +); +genvar i,j; +logic [INPUT_NUM-1:0] sa_local_grt_oh; +logic [INPUT_NUM_IDX_W-1:0] sa_local_grt_idx; + +logic [INPUT_NUM-1:0] vc_ctrl_head_vld_join_arb; // for qos, if no qos, it is same as vc_ctrl_head_vld_i + + +io_port_t [INPUT_NUM-1:0] vc_ctrl_head_i_look_ahead_routing; +logic [INPUT_NUM-1:0][OUTPUT_PORT_NUMBER-1:0] vc_ctrl_head_i_look_ahead_routing_match; + +`ifdef USE_QOS_VALUE +logic [QoS_Value_Width-1:0] vc_ctrl_head_i_qos_value_sel; +`endif + +flit_dec_t vc_ctrl_head_sel; + + +`ifdef USE_QOS_VALUE + + logic [INPUT_NUM-1:0][QoS_Value_Width-1:0] vc_ctrl_head_qos_value; + generate + for(i = 0; i < INPUT_NUM; i++) begin: gen_vc_ctrl_head_qos_value + assign vc_ctrl_head_qos_value [i] = vc_ctrl_head_i[i].qos_value; + end + endgenerate + + priority_req_select + #( + .INPUT_NUM ( INPUT_NUM ), + .INPUT_PRIORITY_W ( QoS_Value_Width ) + ) + sa_local_priority_req_select_u ( + .req_vld_i (vc_ctrl_head_vld_i ), + .req_priority_i (vc_ctrl_head_qos_value ), + .req_vld_o (vc_ctrl_head_vld_join_arb ) + ); + +`else + + assign vc_ctrl_head_vld_join_arb = vc_ctrl_head_vld_i; + +`endif + + +one_hot_rr_arb #( + .N_INPUT (INPUT_NUM), + + .TIMEOUT_UPDATE_EN (1 ), + .TIMEOUT_UPDATE_CYCLE (10) +) +sa_local_rr_arb_u +( + .req_i (vc_ctrl_head_vld_join_arb ), + .update_i (inport_read_enable_sa_stage_i), // use global arbiter result to update: if win the global arbiter update local rr arbiter, or may be no fair + .grt_o (sa_local_grt_oh ), + .grt_idx_o (sa_local_grt_idx ), + + .rstn (rstn ), + .clk (clk ) +); + + + +assign sa_local_vc_id_o = sa_local_grt_idx; +assign sa_local_vc_id_oh_o = sa_local_grt_oh; +`ifdef USE_QOS_VALUE +assign sa_local_qos_value_o = vc_ctrl_head_sel.qos_value; +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM +assign sa_local_dpram_idx_o.dpram_idx = vc_ctrl_head_sel.dpram_used_idx.dpram_idx; +assign sa_local_dpram_idx_o.per_vc_idx = vc_ctrl_head_sel.dpram_used_idx.per_vc_idx; +`endif +assign sa_local_vld_o = |vc_ctrl_head_vld_join_arb; +generate + for(i = 0; i < OUTPUT_PORT_NUMBER; i++) begin + assign sa_local_vld_to_sa_global_o[i] = vc_ctrl_head_vld_join_arb[sa_local_grt_idx] & + vc_ctrl_head_i_look_ahead_routing_match[sa_local_grt_idx][i]; + end +endgenerate + + + +generate + for(i = 0; i < INPUT_NUM; i++) begin + assign vc_ctrl_head_i_look_ahead_routing[i] = vc_ctrl_head_i[i].look_ahead_routing; + end +endgenerate + + +generate + for(i = 0; i < INPUT_NUM; i++) begin: gen_vc_ctrl_head_i_look_ahead_routing_match_i + for(j = 0; j < OUTPUT_PORT_NUMBER; j++) begin: gen_vc_ctrl_head_i_look_ahead_routing_match_j + assign vc_ctrl_head_i_look_ahead_routing_match[i][j] = vc_ctrl_head_i_look_ahead_routing[i] == j[$bits(io_port_t)-1:0]; + end + end +endgenerate + + +onehot_mux +#( + .SOURCE_COUNT(INPUT_NUM ), + .DATA_WIDTH ($bits(flit_dec_t)) +) +onehot_mux_qos_value_sel_u ( + .sel_i (sa_local_grt_oh ), + .data_i (vc_ctrl_head_i ), + .data_o (vc_ctrl_head_sel) +); + + + +endmodule diff --git a/rtl/switch.sv b/rtl/switch.sv new file mode 100644 index 0000000..72b74ea --- /dev/null +++ b/rtl/switch.sv @@ -0,0 +1,279 @@ +module switch +import rvh_noc_pkg::*; +#( + parameter INPUT_PORT_NUM = 5, + parameter OUTPUT_PORT_NUM = 5, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + + parameter type flit_payload_t = logic[256-1:0], + + parameter VC_NUM_INPUT_N = 2, + parameter VC_NUM_INPUT_S = 2, + parameter VC_NUM_INPUT_E = 4, + parameter VC_NUM_INPUT_W = 4, + parameter VC_NUM_INPUT_L = 4, + parameter VC_NUM_INPUT_N_IDX_W = VC_NUM_INPUT_N > 1 ? $clog2(VC_NUM_INPUT_N) : 1, + parameter VC_NUM_INPUT_S_IDX_W = VC_NUM_INPUT_S > 1 ? $clog2(VC_NUM_INPUT_S) : 1, + parameter VC_NUM_INPUT_E_IDX_W = VC_NUM_INPUT_E > 1 ? $clog2(VC_NUM_INPUT_E) : 1, + parameter VC_NUM_INPUT_W_IDX_W = VC_NUM_INPUT_W > 1 ? $clog2(VC_NUM_INPUT_W) : 1, + parameter VC_NUM_INPUT_L_IDX_W = VC_NUM_INPUT_L > 1 ? $clog2(VC_NUM_INPUT_L) : 1 + +) +( + // input flit data from input port buffer + input flit_payload_t [VC_NUM_INPUT_N-1:0] vc_data_head_fromN_i, + input flit_payload_t [VC_NUM_INPUT_S-1:0] vc_data_head_fromS_i, + input flit_payload_t [VC_NUM_INPUT_E-1:0] vc_data_head_fromE_i, + input flit_payload_t [VC_NUM_INPUT_W-1:0] vc_data_head_fromW_i, +`ifdef HAVE_LOCAL_PORT + input flit_payload_t [LOCAL_PORT_NUM-1:0][VC_NUM_INPUT_L-1:0] vc_data_head_fromL_i, +`endif + + // input switch ctrl from SA to ST stage reg + input logic [INPUT_PORT_NUM-1:0] inport_read_enable_st_stage_i, + // input io_port_t [INPUT_PORT_NUM-1:0] inport_read_outport_id_st_stage_i, + input logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] inport_read_vc_id_st_stage_i, + // input io_port_t [INPUT_PORT_NUM-1:0] inport_look_ahead_routing_st_stage_i, + + input logic [OUTPUT_PORT_NUM-1:0] outport_vld_st_stage_i, + input io_port_t [OUTPUT_PORT_NUM-1:0] outport_select_inport_id_st_stage_i, + input logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] outport_vc_id_st_stage_i, + input io_port_t [OUTPUT_PORT_NUM-1:0] outport_look_ahead_routing_st_stage_i, + + // output flit data and look ahead routing to outport + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_pend_o, + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_v_o, + output flit_payload_t [OUTPUT_PORT_NUM-1:0] tx_flit_o, + output logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o, + output io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing_o + +); + +genvar i; + +// link flit clk gating +assign tx_flit_pend_o = '1; // TODO: by now receiver assuming always expecting a new flit + +// select the output flit from per inport +flit_payload_t [INPUT_PORT_NUM-1:0] vc_head_data; + +`ifdef VC_DATA_USE_DUAL_PORT_RAM // for dpram, only one flit would be read from per input_port, and always assign it in slot 0 +assign vc_head_data[0] = vc_data_head_fromN_i[0]; // from N +assign vc_head_data[1] = vc_data_head_fromS_i[0]; // from S +assign vc_head_data[2] = vc_data_head_fromE_i[0]; // from E +assign vc_head_data[3] = vc_data_head_fromW_i[0]; // from W +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_vc_data_head_fromL_i + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_vc_data_head_fromL_i + assign vc_head_data[4+i] = vc_data_head_fromL_i[i][0]; // from L + end + end +endgenerate +`else +assign vc_head_data[0] = vc_data_head_fromN_i[inport_read_vc_id_st_stage_i[0][VC_NUM_INPUT_N_IDX_W-1:0]]; // from N +assign vc_head_data[1] = vc_data_head_fromS_i[inport_read_vc_id_st_stage_i[1][VC_NUM_INPUT_S_IDX_W-1:0]]; // from S +assign vc_head_data[2] = vc_data_head_fromE_i[inport_read_vc_id_st_stage_i[2][VC_NUM_INPUT_E_IDX_W-1:0]]; // from E +assign vc_head_data[3] = vc_data_head_fromW_i[inport_read_vc_id_st_stage_i[3][VC_NUM_INPUT_W_IDX_W-1:0]]; // from W +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_vc_data_head_fromL_i + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_vc_data_head_fromL_i + assign vc_head_data[4+i] = vc_data_head_fromL_i[i][inport_read_vc_id_st_stage_i[4+i][VC_NUM_INPUT_L_IDX_W-1:0]]; // from L + end + end +endgenerate +`endif + + +// map per valid inport head flit to outport + + // map vld +assign tx_flit_v_o = outport_vld_st_stage_i; + + // map receiver vc id +assign tx_flit_vc_id_o = outport_vc_id_st_stage_i; + + // map look ahead routing +assign tx_flit_look_ahead_routing_o = outport_look_ahead_routing_st_stage_i; + + // map data + // to N +always_comb begin + unique case(outport_select_inport_id_st_stage_i[0]) + S: begin + tx_flit_o[0] = vc_head_data[1]; + end + E: begin + tx_flit_o[0] = vc_head_data[2]; + end + W: begin + tx_flit_o[0] = vc_head_data[3]; + end + L0: begin + tx_flit_o[0] = vc_head_data[4]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + tx_flit_o[0] = vc_head_data[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + tx_flit_o[0] = vc_head_data[6]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + tx_flit_o[0] = vc_head_data[7]; + end +`endif + default: begin + tx_flit_o[0] = vc_head_data[1]; + end + endcase +end + + // to S +always_comb begin + unique case(outport_select_inport_id_st_stage_i[1]) + N: begin + tx_flit_o[1] = vc_head_data[0]; + end + E: begin + tx_flit_o[1] = vc_head_data[2]; + end + W: begin + tx_flit_o[1] = vc_head_data[3]; + end + L0: begin + tx_flit_o[1] = vc_head_data[4]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + tx_flit_o[1] = vc_head_data[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + tx_flit_o[1] = vc_head_data[6]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + tx_flit_o[1] = vc_head_data[7]; + end +`endif + default: begin + tx_flit_o[1] = vc_head_data[0]; + end + endcase +end + + // to E +always_comb begin + unique case(outport_select_inport_id_st_stage_i[2]) + W: begin + tx_flit_o[2] = vc_head_data[3]; + end + L0: begin + tx_flit_o[2] = vc_head_data[4]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + tx_flit_o[2] = vc_head_data[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + tx_flit_o[2] = vc_head_data[6]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + tx_flit_o[2] = vc_head_data[7]; + end +`endif + default: begin + tx_flit_o[2] = vc_head_data[3]; + end + endcase +end + + // to W +always_comb begin + unique case(outport_select_inport_id_st_stage_i[3]) + E: begin + tx_flit_o[3] = vc_head_data[2]; + end + L0: begin + tx_flit_o[3] = vc_head_data[4]; + end +`ifdef LOCAL_PORT_NUM_2 + L1: begin + tx_flit_o[3] = vc_head_data[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + tx_flit_o[3] = vc_head_data[6]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + tx_flit_o[3] = vc_head_data[7]; + end +`endif + default: begin + tx_flit_o[3] = vc_head_data[2]; + end + endcase +end + + // to L +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_multi_local_port_in_switch + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_multi_local_port_in_switch + always_comb begin + unique case(outport_select_inport_id_st_stage_i[4+i]) + N: begin + tx_flit_o[4+i] = vc_head_data[0]; + end + S: begin + tx_flit_o[4+i] = vc_head_data[1]; + end + E: begin + tx_flit_o[4+i] = vc_head_data[2]; + end + W: begin + tx_flit_o[4+i] = vc_head_data[3]; + end +`ifdef HAVE_LOCAL_PORT + L0: begin + tx_flit_o[4+i] = vc_head_data[4]; + end +`endif +`ifdef LOCAL_PORT_NUM_2 + L1: begin + tx_flit_o[4+i] = vc_head_data[5]; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + L2: begin + tx_flit_o[4+i] = vc_head_data[6]; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + L3: begin + tx_flit_o[4+i] = vc_head_data[7]; + end +`endif + default: begin + tx_flit_o[4+i] = vc_head_data[0]; + end + endcase + end + end + end +endgenerate + + +endmodule diff --git a/rtl/util/age_order_selector.sv b/rtl/util/age_order_selector.sv new file mode 100755 index 0000000..1604c80 --- /dev/null +++ b/rtl/util/age_order_selector.sv @@ -0,0 +1,143 @@ +`ifndef __AGE_ORDER_SELECTOR_SV__ +`define __AGE_ORDER_SELECTOR_SV__ +/* + * Age Matrix + * | Dependency bit | + * |E| | 0 | 1 | 2 | 3 | + * |N| 0 | 1 | 1 | 1 | 1 | + * |T| 1 | 0 | 1 | 1 | 1 | + * |R| 2 | 0 | 0 | 1 | 1 | + * |Y| 3 | 0 | 0 | 0 | 1 | + * + * age_matrix_r[i][i] : valid signal for entry(i) + * age_matrix_r[i][j] : if entry(j) deponds on entry(i) + * + * */ + +module age_order_selector #( + parameter int unsigned ENTRY_COUNT = 4, + parameter int unsigned ENQ_WIDTH = 1, + parameter int unsigned DEQ_WIDTH = 1, + parameter int unsigned SEL_WIDTH = 1, + localparam int unsigned ENTRY_TAG = $clog2(ENTRY_COUNT) +) ( + // Enqueue + input logic [ENQ_WIDTH-1:0] enq_vld_i, + input logic [ENQ_WIDTH-1:0][ENTRY_TAG-1:0] enq_tag_i, + + // Dequeue + input logic [DEQ_WIDTH-1:0] deq_vld_i, + input logic [DEQ_WIDTH-1:0][ENTRY_TAG-1:0] deq_tag_i, + + // Status + output logic [ENTRY_COUNT-1:0] vld_mask_o, + + // Select Oldest + input logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_mask_i, + output logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_oldest_mask_o, + + // Flush + input logic flush_i, + + input clk, + input rst +); + + logic [ENTRY_COUNT-1:0][ENTRY_COUNT-1:0] age_matrix_r, age_matrix_n; + + logic [ENTRY_COUNT-1:0] vld_mask; + + logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0][ENTRY_COUNT-1:0] sel_age_matrix; + logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_oldest_mask; + + + logic clk_en; + assign clk_en = (|enq_vld_i) | (|deq_vld_i) | flush_i; + + + always_comb begin : get_vld_mask + for (int row = 0; row < ENTRY_COUNT; row++) begin + vld_mask[row] = age_matrix_r[row][row]; + end + end + + + // Sel Oldest Logic + always_comb begin : gen_sel_matrix + for (int i = 0; i < SEL_WIDTH; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + if (row == col) begin + sel_age_matrix[i][row][col] = sel_mask_i[i][col]; + end else begin + sel_age_matrix[i][row][col] = sel_mask_i[i][col] ? age_matrix_r[row][col] : + 1'b1; + end + end + end + end + end + + always_comb begin : gen_sel_oldest_mask + for (int i = 0; i < SEL_WIDTH; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + sel_oldest_mask[i][row] = &sel_age_matrix[i][row]; + end + end + end + + // Update Logic + always_comb begin : update_logic + age_matrix_n = age_matrix_r; + for (int i = 0; i < ENQ_WIDTH; i++) begin + if (enq_vld_i[i]) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_n[enq_tag_i[i]][col] = ~age_matrix_r[col][col]; // Set Dependency + for(int j = 0; j < i; j++) begin + if(enq_vld_i[j]) begin + age_matrix_n[enq_tag_i[i]][enq_tag_i[j]] = 1'b0; + end + end + end + end + end + for (int i = 0; i < DEQ_WIDTH; i++) begin + if (deq_vld_i[i]) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + age_matrix_n[row][deq_tag_i[i]] = deq_tag_i[i] != + row[ENTRY_TAG-1:0]; // Clear Dependency + end + end + end + if (flush_i) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_n[row][col] = 1'b0; + end + end + end + end + + always_ff @(posedge clk) begin : age_matrix_dff + if (rst) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_r[row][col] <= 1'b0; + end + end + end else begin + if (clk_en) begin + age_matrix_r <= age_matrix_n; + end + end + end + + + // Output + assign vld_mask_o = vld_mask; + assign sel_oldest_mask_o = sel_oldest_mask; + + +endmodule : age_order_selector + +`endif \ No newline at end of file diff --git a/rtl/util/age_order_selector_with_head.sv b/rtl/util/age_order_selector_with_head.sv new file mode 100755 index 0000000..a68813c --- /dev/null +++ b/rtl/util/age_order_selector_with_head.sv @@ -0,0 +1,203 @@ +`ifndef __AGE_ORDER_SELECTOR_WITH_HEAD_SV__ +`define __AGE_ORDER_SELECTOR_WITH_HEAD_SV__ +/* + * Age Matrix + * | Dependency bit | + * |E| | 0 | 1 | 2 | 3 | + * |N| 0 | 1 | 1 | 1 | 1 | + * |T| 1 | 0 | 1 | 1 | 1 | + * |R| 2 | 0 | 0 | 1 | 1 | + * |Y| 3 | 0 | 0 | 0 | 1 | + * + * age_matrix_r[i][i] : valid signal for entry(i) + * age_matrix_r[i][j] : if entry(j) deponds on entry(i) + * + * */ + +module age_order_selector_with_head #( + parameter int unsigned ENTRY_COUNT = 4, + parameter int unsigned ENQ_WIDTH = 1, + parameter int unsigned DEQ_WIDTH = 1, + parameter int unsigned SEL_WIDTH = 1, + parameter int unsigned HEAD_PTR_COUNT = 1, + localparam int unsigned ENTRY_TAG = $clog2(ENTRY_COUNT) +) ( + // Enqueue + input logic [ENQ_WIDTH-1:0] enq_vld_i, + input logic [ENQ_WIDTH-1:0][ENTRY_TAG-1:0] enq_tag_i, + + // Dequeue + input logic [DEQ_WIDTH-1:0] deq_vld_i, + input logic [DEQ_WIDTH-1:0][ENTRY_TAG-1:0] deq_tag_i, + + // Status + output logic [ENTRY_COUNT-1:0] vld_mask_o, + output logic [HEAD_PTR_COUNT-1:0][ENTRY_COUNT-1:0] oldest_mask_o, + + // Select Oldest + input logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_mask_i, + output logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_oldest_mask_o, + + // Flush + input logic flush_i, + + input clk, + input rst +); + + logic [ENTRY_COUNT-1:0][ENTRY_COUNT-1:0] age_matrix_r, age_matrix_n; + + logic [ENTRY_COUNT-1:0] vld_mask; + + logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0][ENTRY_COUNT-1:0] sel_age_matrix; + logic [SEL_WIDTH-1:0][ENTRY_COUNT-1:0] sel_oldest_mask; + + logic [HEAD_PTR_COUNT-1:0][ENTRY_COUNT-1:0] oldest_mask_r, oldest_mask_n; + logic [HEAD_PTR_COUNT-1:0][ENTRY_COUNT-1:0] masked_vld_mask; + logic [HEAD_PTR_COUNT-1:0][ENTRY_COUNT-1:0][ENTRY_COUNT-1:0] head_masked_age_matrix; + + + + logic clk_en; + assign clk_en = (|enq_vld_i) | (|deq_vld_i) | flush_i; + + + always_comb begin : get_vld_mask + for (int row = 0; row < ENTRY_COUNT; row++) begin + vld_mask[row] = age_matrix_r[row][row]; + end + end + + + // Sel Oldest Logic + always_comb begin : gen_sel_matrix + for (int i = 0; i < SEL_WIDTH; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + if (row == col) begin + sel_age_matrix[i][row][col] = sel_mask_i[i][col]; + end else begin + sel_age_matrix[i][row][col] = sel_mask_i[i][col] ? age_matrix_r[row][col] : + 1'b1; + end + end + end + end + end + + always_comb begin : gen_sel_oldest_mask + for (int i = 0; i < SEL_WIDTH; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + sel_oldest_mask[i][row] = &sel_age_matrix[i][row]; + end + end + end + + // Update Logic + always_comb begin : update_logic + age_matrix_n = age_matrix_r; + for(int i = 0; i < ENQ_WIDTH; i++) begin + if (enq_vld_i[i]) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_n[enq_tag_i[i]][col] = ~age_matrix_r[col][col]; // Set Dependency + for(int j = 0; j < i; j++) begin + if(enq_vld_i[j]) begin + age_matrix_n[enq_tag_i[i]][enq_tag_i[j]] = 1'b0; + end + end + end + end + end + for(int i = 0 ; i < DEQ_WIDTH; i++) begin + if (deq_vld_i[i]) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + age_matrix_n[row][deq_tag_i[i]] = deq_tag_i[i] != row[ENTRY_TAG-1:0]; // Clear Dependency + end + end + end + if (flush_i) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_n[row][col] = 1'b0; + end + end + end + end + + always_ff @(posedge clk) begin : age_matrix_dff + if (rst) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + age_matrix_r[row][col] <= 1'b0; + end + end + end else begin + if (clk_en) begin + age_matrix_r <= age_matrix_n; + end + end + end + + // n-head Logic + + always_comb begin : gen_masked_vld_mask + for (int i = 0; i < HEAD_PTR_COUNT; i++) begin + if (i == 0) begin + masked_vld_mask[i] = vld_mask; + end else begin + masked_vld_mask[i] = masked_vld_mask[i-1] & ~oldest_mask_n[i-1]; + end + end + end + + always_comb begin : gen_head_masked_matrix + for (int i = 0; i < HEAD_PTR_COUNT; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + for (int col = 0; col < ENTRY_COUNT; col++) begin + if (i == 0) begin + head_masked_age_matrix[i][row][col] = age_matrix_n[row][col]; + end else begin + if (col == row) begin + head_masked_age_matrix[i][row][col] = masked_vld_mask[i][col]; + end else begin + head_masked_age_matrix[i][row][col] = masked_vld_mask[i][col] ? + head_masked_age_matrix[i-1][row][col] : 1'b1; + end + end + end + end + end + end + + always_comb begin : gen_n_head_mask + for (int i = 0; i < HEAD_PTR_COUNT; i++) begin + for (int row = 0; row < ENTRY_COUNT; row++) begin + oldest_mask_n[i][row] = &head_masked_age_matrix[i][row]; + end + if (flush_i) begin + oldest_mask_n = {HEAD_PTR_COUNT * ENTRY_COUNT{1'b0}}; + end + end + end + + + always_ff @(posedge clk) begin : oldest_mask_dff + if (rst) begin + oldest_mask_r <= {HEAD_PTR_COUNT * ENTRY_COUNT{1'b0}}; + end else begin + if (clk_en) begin + oldest_mask_r <= oldest_mask_n; + end + end + end + + + // Output + assign oldest_mask_o = oldest_mask_r; + assign vld_mask_o = vld_mask; + assign sel_oldest_mask_o = sel_oldest_mask; + + +endmodule : age_order_selector_with_head + +`endif \ No newline at end of file diff --git a/rtl/util/commoncell/.gitmodules b/rtl/util/commoncell/.gitmodules new file mode 100644 index 0000000..89c2b99 --- /dev/null +++ b/rtl/util/commoncell/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/pico"] + path = tools/pico + url = git@gitlab.com:series-han/development/pico.git diff --git a/rtl/util/commoncell/CommonCell.yaml b/rtl/util/commoncell/CommonCell.yaml new file mode 100644 index 0000000..460d11e --- /dev/null +++ b/rtl/util/commoncell/CommonCell.yaml @@ -0,0 +1,5 @@ +Name: CommonCell +Dependency: + - src/StdDFF/StdDFF.yaml + - src/Basic/Basic.yaml + - src/Queue/Queue.yaml diff --git a/rtl/util/commoncell/README.md b/rtl/util/commoncell/README.md new file mode 100644 index 0000000..cc4478e --- /dev/null +++ b/rtl/util/commoncell/README.md @@ -0,0 +1,92 @@ +# CommonCell + +Common logic cell for RTL encoding. + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://gitlab.com/series-han/hardware/commoncell.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://gitlab.com/series-han/hardware/commoncell/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/rtl/util/commoncell/doc/.gitignore b/rtl/util/commoncell/doc/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/rtl/util/commoncell/env/sourceme b/rtl/util/commoncell/env/sourceme new file mode 100644 index 0000000..049bd52 --- /dev/null +++ b/rtl/util/commoncell/env/sourceme @@ -0,0 +1,11 @@ +#!/bin/sh + +CURDIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd ) + +PROJ_ROOT=$CURDIR/../ + +PICO_PATH=$PROJ_ROOT/tools/pico/ + +export PROJ_ROOT + +export PATH=$PICO_PATH:$PATH diff --git a/rtl/util/commoncell/src/Basic/Basic.yaml b/rtl/util/commoncell/src/Basic/Basic.yaml new file mode 100644 index 0000000..02937f2 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/Basic.yaml @@ -0,0 +1,67 @@ +Name: Basic +Dependency: + - ../StdDFF/StdDFF.yaml +Module: + - name: CountOne + description: CountOne + language: SystemVerilog + rtl: + - hw/CountOne.v + sim: + - dv/CountOne_tb.v + + - name: MuxOH + description: One-hot Mux + language: SystemVerilog + rtl: + - hw/MuxOH.v + sim: + - dv/MuxOH_tb.v + + - name: OH2UInt + description: One-hot to UInt + language: SystemVerilog + rtl: + - hw/OH2UInt.v + dependency: + - CountOne + + - name: PriorityMux + description: Priority Mux + language: SystemVerilog + rtl: + - hw/PriorityMux.v + dependency: + - MuxOH + + - name: SyncFIFO + description: Sync FIFO + language: SystemVerilog + rtl: + - hw/SyncFIFO.v + sim: + - dv/SyncFIFO_tb.v + dependency: + - StdDFF + + - name: StreamFIFO + description: StreamFIFO + language: SystemVerilog + rtl: + - hw/StreamFIFO.v + sim: + - dv/StreamFIFO_tb.v + dependency: + - SyncFIFO + + - name: PLRU + description: PLRU + language: SystemVerilog + rtl: + - hw/PLRUTree.v + - hw/PLRU.v + sim: + - dv/PLRU_tb.v + dependency: + - StdDFF + diff --git a/rtl/util/commoncell/src/Basic/dv/CountOne_tb.v b/rtl/util/commoncell/src/Basic/dv/CountOne_tb.v new file mode 100644 index 0000000..c121316 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/dv/CountOne_tb.v @@ -0,0 +1,59 @@ +module CountOne_tb; + + // Parameters + localparam int unsigned InputWidth = 8; + localparam int unsigned CountWidth = $clog2(InputWidth + 1); + + // Ports + reg [InputWidth-1:0] bits_i; + wire [CountWidth-1:0] cnt_o; + + CountOne #( + .InputWidth(InputWidth) + ) u_CountOne ( + .bits_i(bits_i), + .cnt_o (cnt_o) + ); + + function automatic [CountWidth-1:0] GlodenModel; + input bit [InputWidth-1:0] in; + bit [CountWidth-1:0] sum; + begin + sum = 0; + for (int i = 0; i < InputWidth; i++) begin + sum = sum + in[i]; + end + GlodenModel = sum; + end + endfunction + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("CountOne_tb.fsdb"); + $fsdbDumpvars(0, CountOne_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + initial begin : RANDOM_TEST + begin + repeat (10000) begin : GEN_CASE + bits_i = $urandom_range(0, InputWidth - 1); + #100; + NOT_EQUAL : + assert (cnt_o == GlodenModel(bits_i)) + else begin + $error("Input[%b], Output[%d], Gloden[%d]\n", bits_i, cnt_o, GlodenModel(bits_i)); + break; + end + end + #100; + $fatal("PASS\n"); + $finish; + end + end + + +endmodule diff --git a/rtl/util/commoncell/src/Basic/dv/MuxOH_tb.v b/rtl/util/commoncell/src/Basic/dv/MuxOH_tb.v new file mode 100644 index 0000000..98767b7 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/dv/MuxOH_tb.v @@ -0,0 +1,66 @@ +module MuxOH_tb; + + // Parameters + localparam int unsigned InputWidth = 8; + localparam int unsigned DataWidth = 8; + + // Ports + reg [InputWidth-1:0] sel_i; + reg [InputWidth-1:0][DataWidth-1:0] data_i; + wire [DataWidth-1:0] data_o; + + MuxOH #( + .InputWidth(InputWidth), + .DataWidth (DataWidth) + ) u_MuxOH ( + .sel_i (sel_i), + .data_i(data_i), + .data_o(data_o) + ); + + function automatic [DataWidth-1:0] GlodenModel; + input bit [InputWidth-1:0] sel; + input bit [InputWidth-1:0][DataWidth-1:0] data; + bit [DataWidth-1:0] result; + begin + for (int i = 0; i < InputWidth; i++) begin + if (sel[i]) begin + result = data[i]; + end + end + GlodenModel = result; + end + endfunction + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("MuxOH_tb.fsdb"); + $fsdbDumpvars(0, MuxOH_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + + initial begin + repeat (10000) begin : GEN_CASE + sel_i = 1 << $urandom_range(0, InputWidth - 1); + for (int i = 0; i < InputWidth; i++) begin + data_i[i] = $urandom(); + end + #100; + NOT_EQUAL : + assert (data_o == GlodenModel(sel_i, data_i)) + else begin + $fatal("Input[%b], Output[%d], Gloden[%d]\n", sel_i, data_o, GlodenModel(sel_i, data_i)); + break; + end + end + #100; + $info("PASS\n"); + $finish; + end + + +endmodule diff --git a/rtl/util/commoncell/src/Basic/dv/PLRU_tb.v b/rtl/util/commoncell/src/Basic/dv/PLRU_tb.v new file mode 100644 index 0000000..800dd28 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/dv/PLRU_tb.v @@ -0,0 +1,49 @@ +module PLRU_tb; + + // Parameters + localparam ENTRY_COUNT = 4; + + // Ports + reg [ENTRY_COUNT-1:0] access_mask_i; + wire [ENTRY_COUNT-1:0] least_used_mask_o; + reg clk = 0; + reg rstn = 0; + + PLRU #( + .ENTRY_COUNT(ENTRY_COUNT) + ) PLRU_dut ( + .access_mask_i(access_mask_i), + .least_used_mask_o(least_used_mask_o), + .clk(clk), + .rstn(rstn) + ); + + initial begin + begin + #10 rstn = 1'b1; + repeat (10000) begin + @(negedge clk); + if (|access_mask_i) begin + $info("\n Access Entry[%d], PLRU Change to [%d]\n", $countones(access_mask_i-1), $countones( + least_used_mask_o - 1)); + end + access_mask_i = 1 << $urandom_range(0, ENTRY_COUNT - 1); + @(posedge clk); + end + $finish; + end + end + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("PLRU_tb.fsdb"); + $fsdbDumpvars(0, PLRU_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + always #20 clk = !clk; + +endmodule diff --git a/rtl/util/commoncell/src/Basic/dv/StreamFIFO_tb.v b/rtl/util/commoncell/src/Basic/dv/StreamFIFO_tb.v new file mode 100644 index 0000000..29a51e9 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/dv/StreamFIFO_tb.v @@ -0,0 +1,83 @@ +module StreamFIFO_tb; + + // Parameters + localparam int unsigned Depth = 8; + localparam int unsigned WordWidth = 64; + + // Ports + reg enq_vld_i = 0; + reg [WordWidth-1:0] enq_payload_i; + wire enq_rdy_o; + wire deq_vld_o; + wire [WordWidth-1:0] deq_payload_o; + reg deq_rdy_i = 0; + reg flush_i = 0; + reg clk = 0; + reg rstn = 0; + + bit [WordWidth-1:0] golden_fifo[$]; + bit [WordWidth-1:0] random_payload; + bit [WordWidth-1:0] golden_fifo_front; + + int iter = 1000000; + + + initial begin + begin + #10 rstn = 1'b1; + repeat (iter) begin : random_test + @(negedge clk); + enq_vld_i = 1'b0; + deq_rdy_i = 1'b0; + if ($urandom_range(0, 1) && deq_vld_o) begin : test_deq + deq_rdy_i = 1'b1; + golden_fifo_front = golden_fifo.pop_front(); + CHECK_EQUALATION : + assert (deq_payload_o == golden_fifo_front) + else begin + $fatal("\n Error: Fail when check equalation, ours[%x] -- gloden[%x]", deq_payload_o, + golden_fifo_front); + end + ; + end + if ($urandom_range(0, 1) && enq_rdy_o) begin : test_push + random_payload = $urandom(); + enq_vld_i = 1'b1; + enq_payload_i = random_payload; + golden_fifo.push_back(random_payload); + end + end + $info("\n PASS after %d iter \n", iter); + $finish; + end + end + + + StreamFIFO #( + .Depth(Depth), + .WordWidth(WordWidth) + ) StreamFIFO_dut ( + .enq_vld_i(enq_vld_i), + .enq_payload_i(enq_payload_i), + .enq_rdy_o(enq_rdy_o), + .deq_vld_o(deq_vld_o), + .deq_payload_o(deq_payload_o), + .deq_rdy_i(deq_rdy_i), + .flush_i(flush_i), + .clk(clk), + .rstn(rstn) + ); + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("StreamFIFO_tb.fsdb"); + $fsdbDumpvars(0, StreamFIFO_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + always #20 clk = !clk; + +endmodule diff --git a/rtl/util/commoncell/src/Basic/dv/SyncFIFO_tb.v b/rtl/util/commoncell/src/Basic/dv/SyncFIFO_tb.v new file mode 100644 index 0000000..561e118 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/dv/SyncFIFO_tb.v @@ -0,0 +1,83 @@ +module SyncFIFO_tb; + + // Parameters + localparam int unsigned Depth = 8; + localparam int unsigned WordWidth = 32; + + // Ports + reg push_i = 0; + reg [WordWidth-1:0] push_payload_i; + reg pop_i = 0; + wire [WordWidth-1:0] pop_payload_o; + wire full_o; + wire empty_o; + reg flush_i = 0; + reg clk = 0; + reg rstn = 0; + + bit [WordWidth-1:0] golden_fifo[$]; + bit [WordWidth-1:0] random_payload; + bit [WordWidth-1:0] golden_fifo_front; + + int iter = 1000000; + + initial begin + begin + #10 rstn = 1'b1; + repeat (iter) begin : random_test + @(negedge clk); + pop_i = 1'b0; + push_i = 1'b0; + if ($urandom_range(0, 1) && ~empty_o) begin : test_pop + pop_i = 1'b1; + golden_fifo_front = golden_fifo.pop_front(); + CHECK_EQUALATION : + assert (pop_payload_o == golden_fifo_front) + else begin + $fatal("\n Error: Fail when check equalation, ours[%x] -- gloden[%x]", pop_payload_o, + golden_fifo_front); + end + ; + end + if ($urandom_range(0, 1) && ~full_o) begin : test_push + random_payload = $urandom(); + push_i = 1'b1; + push_payload_i = random_payload; + golden_fifo.push_back(random_payload); + end + end + $info("\n PASS after %d iter \n", iter); + $finish; + end + end + + SyncFIFO #( + .Depth(Depth), + .WordWidth(WordWidth) + ) SyncFIFO_dut ( + .push_i(push_i), + .push_payload_i(push_payload_i), + .pop_i(pop_i), + .pop_payload_o(pop_payload_o), + .full_o(full_o), + .empty_o(empty_o), + .flush_i(flush_i), + .clk(clk), + .rstn(rstn) + ); + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("SyncFIFO_tb.fsdb"); + $fsdbDumpvars(0, SyncFIFO_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + + + always #20 clk = !clk; + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/CountOne.v b/rtl/util/commoncell/src/Basic/hw/CountOne.v new file mode 100644 index 0000000..18206de --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/CountOne.v @@ -0,0 +1,42 @@ +module CountOne #( + parameter int unsigned InputWidth = 8, + localparam int unsigned CountWidth = $clog2(InputWidth + 1) +) ( + input wire [InputWidth-1:0] bits_i, + output wire [CountWidth-1:0] cnt_o +); + localparam int unsigned PaddedWidth = 1 << $clog2(InputWidth); + localparam int unsigned ChildCountWidth = PaddedWidth == 1 ? 1 : $clog2(PaddedWidth); + + wire [ChildCountWidth-1:0] leftChildCount, rightChildCount; + + + generate + if (InputWidth == 1) begin : gen_single_node + assign leftChildCount = 1'b0; + assign rightChildCount = bits_i; + end else if (InputWidth == 2) begin : gen_leaf_node + assign leftChildCount = bits_i[0]; + assign rightChildCount = bits_i[1]; + end else begin : gen_non_leaf_node + wire [PaddedWidth-1:0] paddedBits; + // Zero pad the input bits to next power of two + assign paddedBits = {{PaddedWidth - InputWidth{1'b0}}, bits_i}; + CountOne #( + .InputWidth(PaddedWidth / 2) + ) u_leftChild ( + .bits_i(paddedBits[PaddedWidth-1:PaddedWidth/2]), + .cnt_o (leftChildCount) + ); + CountOne #( + .InputWidth(PaddedWidth / 2) + ) u_rightChild ( + .bits_i(paddedBits[PaddedWidth/2-1:0]), + .cnt_o (rightChildCount) + ); + end + endgenerate + + assign cnt_o = leftChildCount + rightChildCount; + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/MuxOH.v b/rtl/util/commoncell/src/Basic/hw/MuxOH.v new file mode 100644 index 0000000..e39dbfa --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/MuxOH.v @@ -0,0 +1,21 @@ +module MuxOH #( + parameter int unsigned InputWidth = 8, + parameter int unsigned DataWidth = 8 +) ( + input wire [InputWidth-1:0] sel_i, + input wire [InputWidth-1:0][DataWidth-1:0] data_i, + output wire [DataWidth-1:0] data_o +); + + wire [DataWidth-1:0][InputWidth-1:0] dataT; + + generate + for (genvar i = 0; i < DataWidth; i++) begin : gen_row + for (genvar j = 0; j < InputWidth; j++) begin : gen_col + assign dataT[i][j] = data_i[j][i]; + end + assign data_o[i] = |(dataT[i] & sel_i); + end + endgenerate + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/OH2UInt.v b/rtl/util/commoncell/src/Basic/hw/OH2UInt.v new file mode 100644 index 0000000..927d8ad --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/OH2UInt.v @@ -0,0 +1,25 @@ +module OH2UInt #( + parameter int unsigned InputWidth = 8, + localparam int unsigned OutputWidth = InputWidth > 1 ? $clog2(InputWidth) : 1 +) ( + input wire [ InputWidth-1:0] oh_i, + output wire [OutputWidth-1:0] result_o +); + + localparam int unsigned CountWidth = $clog2(InputWidth + 1); + + wire [InputWidth-1:0] maskLow; + wire [CountWidth-1:0] count; + + + assign maskLow = oh_i - 1'b1; + assign result_o = count[OutputWidth-1:0]; + + CountOne #( + .InputWidth(InputWidth) + ) CountOne_dut ( + .bits_i(maskLow), + .cnt_o (count) + ); + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/PLRU.v b/rtl/util/commoncell/src/Basic/hw/PLRU.v new file mode 100644 index 0000000..9771011 --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/PLRU.v @@ -0,0 +1,48 @@ +module PLRU #( + parameter ENTRY_COUNT = 4 +) ( + input [ENTRY_COUNT-1:0] access_mask_i, + output [ENTRY_COUNT-1:0] least_used_mask_o, + input clk, + input rstn +); + + localparam PLRUTreeLvl = $clog2(ENTRY_COUNT); + localparam NodeCount = ENTRY_COUNT - 1; + + generate + if (ENTRY_COUNT == 1) begin : gen_one_entry_case + assign least_used_mask_o = 1'b1; + end else begin : gen_normal_case + + wire plru_nodes_clk_en; + wire [NodeCount-1:0] plru_nodes_d; + reg [NodeCount-1:0] plru_nodes_q; + + assign plru_nodes_clk_en = |access_mask_i; + + DFFRE #( + .Width(NodeCount) + ) u_plru_nodes_DFFRE ( + .CLK(clk), + .RSTN(rstn), + .DRST({NodeCount{1'b0}}), + .EN(plru_nodes_clk_en), + .D(plru_nodes_d), + .Q(plru_nodes_q) + ); + PLRUTree #( + .TREE_LVL_COUNT(PLRUTreeLvl) + ) u_PLRUTree ( + .access_mask_i(access_mask_i), + .plru_old_node_i(plru_nodes_q), + .plru_new_node_o(plru_nodes_d), + .node_en_i(1'b1), + .least_used_mask_o(least_used_mask_o), + .clk(clk), + .rstn(rstn) + ); + end + endgenerate + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/PLRUTree.v b/rtl/util/commoncell/src/Basic/hw/PLRUTree.v new file mode 100644 index 0000000..2dbe67c --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/PLRUTree.v @@ -0,0 +1,64 @@ +module PLRUTree #( + parameter TREE_LVL_COUNT = 3, + localparam ENTRY_COUNT = (1 << TREE_LVL_COUNT), + localparam NODE_COUNT = ENTRY_COUNT - 1 +) ( + input [ENTRY_COUNT-1:0] access_mask_i, + input [NODE_COUNT-1:0] plru_old_node_i, + output [NODE_COUNT-1:0] plru_new_node_o, + input node_en_i, + output [ENTRY_COUNT-1:0] least_used_mask_o, + input clk, + input rstn +); + + wire node_toggle_en; + wire node; + + wire left_child_node_en; + wire right_child_node_en; + + wire access_left; + wire access_right; + + assign node = plru_old_node_i[0]; + assign node_toggle_en = (~node & access_left) | (node & access_right); + assign plru_new_node_o[0] = node_toggle_en ? ~node : node; + + assign access_left = |access_mask_i[ENTRY_COUNT/2-1:0]; + assign access_right = |access_mask_i[ENTRY_COUNT-1:ENTRY_COUNT/2]; + + assign left_child_node_en = node_en_i & ~node; + assign right_child_node_en = node_en_i & node; + + generate + if (TREE_LVL_COUNT > 1) begin : gen_child_tree + PLRUTree #( + .TREE_LVL_COUNT(TREE_LVL_COUNT - 1) + ) u_left_child_PLRUTree ( + .access_mask_i(access_mask_i[ENTRY_COUNT/2-1:0]), + .plru_old_node_i(plru_old_node_i[(NODE_COUNT-1)/2:1]), + .plru_new_node_o(plru_new_node_o[(NODE_COUNT-1)/2:1]), + .node_en_i(left_child_node_en), + .least_used_mask_o(least_used_mask_o[ENTRY_COUNT/2-1:0]), + .clk(clk), + .rstn(rstn) + ); + PLRUTree #( + .TREE_LVL_COUNT(TREE_LVL_COUNT - 1) + ) u_right_child_PLRUTree ( + .access_mask_i(access_mask_i[ENTRY_COUNT-1:ENTRY_COUNT/2]), + .plru_old_node_i(plru_old_node_i[NODE_COUNT-1:(NODE_COUNT+1)/2]), + .plru_new_node_o(plru_new_node_o[NODE_COUNT-1:(NODE_COUNT+1)/2]), + .node_en_i(right_child_node_en), + .least_used_mask_o(least_used_mask_o[ENTRY_COUNT-1:ENTRY_COUNT/2]), + .clk(clk), + .rstn(rstn) + ); + end else begin : gen_leaf + assign least_used_mask_o[0] = left_child_node_en; + assign least_used_mask_o[1] = right_child_node_en; + end + endgenerate + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/PriorityMux.v b/rtl/util/commoncell/src/Basic/hw/PriorityMux.v new file mode 100644 index 0000000..eaa2f6d --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/PriorityMux.v @@ -0,0 +1,24 @@ +module PriorityMux #( + parameter int unsigned InputWidth = 8, + parameter int unsigned DataWidth = 8 +) ( + input wire [InputWidth-1:0] sel_i, + input wire [InputWidth-1:0][DataWidth-1:0] data_i, + output wire [DataWidth-1:0] data_o +); + + wire [InputWidth-1:0] sel_oh; + + assign sel_oh = sel_i & (~(sel_i - 1)); + + MuxOH #( + .InputWidth(InputWidth), + .DataWidth (DataWidth) + ) u_MuxOH ( + .sel_i (sel_oh), + .data_i(data_i), + .data_o(data_o) + ); + + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/StreamFIFO.v b/rtl/util/commoncell/src/Basic/hw/StreamFIFO.v new file mode 100644 index 0000000..4b02ccd --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/StreamFIFO.v @@ -0,0 +1,52 @@ +module StreamFIFO #( + parameter int unsigned Depth = 8, + parameter int unsigned WordWidth = 64 +) ( + input wire enq_vld_i, + input wire [WordWidth-1:0] enq_payload_i, + output wire enq_rdy_o, + output wire deq_vld_o, + output wire [WordWidth-1:0] deq_payload_o, + input wire deq_rdy_i, + input wire flush_i, + input wire clk, + input wire rstn +); + + wire push; + wire [WordWidth-1:0] push_payload; + wire pop; + wire [WordWidth-1:0] pop_payload; + wire full; + wire empty; + + wire enq_fire; + wire deq_fire; + + assign enq_rdy_o = ~full; + assign enq_fire = enq_vld_i & enq_rdy_o; + assign push = enq_fire; + assign push_payload = enq_payload_i; + + assign pop = deq_fire; + assign deq_vld_o = ~empty; + assign deq_fire = deq_vld_o & deq_rdy_i; + assign deq_payload_o = pop_payload; + + SyncFIFO #( + .Depth(Depth), + .WordWidth(WordWidth) + ) u_SyncFIFO ( + .push_i(push), + .push_payload_i(push_payload), + .pop_i(pop), + .pop_payload_o(pop_payload), + .full_o(full), + .empty_o(empty), + .flush_i(flush_i), + .clk(clk), + .rstn(rstn) + ); + + +endmodule diff --git a/rtl/util/commoncell/src/Basic/hw/SyncFIFO.v b/rtl/util/commoncell/src/Basic/hw/SyncFIFO.v new file mode 100644 index 0000000..5b574ee --- /dev/null +++ b/rtl/util/commoncell/src/Basic/hw/SyncFIFO.v @@ -0,0 +1,129 @@ +module SyncFIFO #( + parameter int unsigned Depth = 8, + parameter int unsigned WordWidth = 64 +) ( + input wire push_i, + input wire [WordWidth-1:0] push_payload_i, + input wire pop_i, + output wire [WordWidth-1:0] pop_payload_o, + output wire full_o, + output wire empty_o, + input wire flush_i, + input wire clk, + input wire rstn +); + + localparam int unsigned PtrWidth = $clog2(Depth); + + wire [Depth-1:0] payload_ram_clk_en; + wire [Depth-1:0][WordWidth-1:0] payload_ram_d, payload_ram_q; + + wire head_clk_en; + reg head_flag_d, head_flag_q; + reg [PtrWidth-1:0] head_ptr_d, head_ptr_q; + + wire tail_clk_en; + reg tail_flag_d, tail_flag_q; + reg [PtrWidth-1:0] tail_ptr_d, tail_ptr_q; + + wire ptr_equal; + wire flag_equal; + + assign head_clk_en = pop_i | flush_i; + assign tail_clk_en = push_i | flush_i; + + generate + for (genvar i = 0; i < Depth; i++) begin : gen_payload_update_logic + assign payload_ram_clk_en[i] = (push_i & ~flush_i) & (tail_ptr_q == i); + assign payload_ram_d[i] = payload_ram_clk_en[i] ? push_payload_i : payload_ram_q[i]; + end + endgenerate + + always @(*) begin : tail_update_logic + {tail_flag_d, tail_ptr_d} = {tail_flag_q, tail_ptr_q}; + if (push_i) begin + if (tail_ptr_q == Depth - 1) begin + {tail_flag_d, tail_ptr_d} = {~tail_flag_q, {PtrWidth{1'b0}}}; + end else begin + tail_ptr_d = tail_ptr_q + 1'b1; + end + end + if (flush_i) begin + {tail_flag_d, tail_ptr_d} = {PtrWidth + 1{1'b0}}; + end + end + + always @(*) begin : head_update_logic + {head_flag_d, head_ptr_d} = {head_flag_q, head_ptr_q}; + if (pop_i) begin + if (head_ptr_q == Depth - 1) begin + {head_flag_d, head_ptr_d} = {~head_flag_q, {PtrWidth{1'b0}}}; + end else begin + head_ptr_d = head_ptr_q + 1'b1; + end + end + if (flush_i) begin + {head_flag_d, head_ptr_d} = {PtrWidth + 1{1'b0}}; + end + end + + assign flag_equal = head_flag_q == tail_flag_q; + assign ptr_equal = head_ptr_q == tail_ptr_q; + + assign full_o = (~flag_equal & ptr_equal & ~pop_i); + assign empty_o = flag_equal & ptr_equal; + assign pop_payload_o = payload_ram_q[head_ptr_q]; + + DFFRE #( + .Width(PtrWidth + 1) + ) u_head_dff ( + .CLK(clk), + .RSTN(rstn), + .EN(head_clk_en), + .DRST({PtrWidth + 1{1'b0}}), + .D({head_flag_d, head_ptr_d}), + .Q({head_flag_q, head_ptr_q}) + ); + + DFFRE #( + .Width(PtrWidth + 1) + ) u_tail_dff ( + .CLK(clk), + .RSTN(rstn), + .EN(tail_clk_en), + .DRST({PtrWidth + 1{1'b0}}), + .D({tail_flag_d, tail_ptr_d}), + .Q({tail_flag_q, tail_ptr_q}) + ); + + generate + for (genvar i = 0; i < Depth; i++) begin : gen_payload_dff + DFFE #( + .Width(WordWidth) + ) u_payload_dff ( + .CLK(clk), + .EN (payload_ram_clk_en[i]), + .D (payload_ram_d[i]), + .Q (payload_ram_q[i]) + ); + end + endgenerate + +`ifndef SYNTHESIS + + default disable iff (~rstn); CHECK_POP_WHEN_EMPTY : + assert property (@(posedge clk) pop_i |-> ~empty_o) + else begin + $error("Pop when empty\n"); + end + + CHECK_PUSH_WHEN_FULL : + assert property (@(posedge clk) push_i |-> ~full_o) + else begin + $error("Push when full\n"); + end + +`endif + + +endmodule diff --git a/rtl/util/commoncell/src/Queue/Queue.yaml b/rtl/util/commoncell/src/Queue/Queue.yaml new file mode 100644 index 0000000..774c628 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/Queue.yaml @@ -0,0 +1,58 @@ +Name: Queue +Dependency: + - ../StdDFF/StdDFF.yaml + - ../Basic/Basic.yaml +Module: + - name: QueueManager + description: Queue Ptr Manager + language: SystemVerilog + rtl: + - hw/QueueManager.v + dependency: + - CountOne + - StdDFF + + - name: StaticPrioritySelector + description: Static Priority Selector + language: SystemVerilog + rtl: + - hw/StaticPrioritySelector.v + sim: + - dv/StaticPrioritySelector_tb.v + + - name: AgeMatrixSelector + description: Age Matrix selector to select in age order + language: SystemVerilog + rtl: + - hw/AgeMatrixSelector.v + # sim: + # - dv/AgeMatrixSelector_tb.v + dependency: + - MuxOH + + - name: MultiPortStreamFIFO + description: MultiPortStream FIFO + language: SystemVerilog + rtl: + - hw/FIFO/MultiPortStreamFIFO.v + sim: + - dv/FIFO/MultiPortStreamFIFO_tb.v + dependency: + - QueueManager + + - name: FIAOWithQueueManager + description: FIAOWithQueueManager + language: SystemVerilog + rtl: + - hw/FIAO/FIAOWithQueueManager.v + dependency: + - QueueManager + + # - name: FIAOWithAgeMatrix + # description: FIAOWithAgeMatrix + # language: SystemVerilog + # rtl: + # - hw/FIAO/FIAOWithAgeMatrix.v + # dependency: + # - OH2UInt + # - AgeMatrixSelector \ No newline at end of file diff --git a/rtl/util/commoncell/src/Queue/dv/AgeMatrixSelector_tb.v b/rtl/util/commoncell/src/Queue/dv/AgeMatrixSelector_tb.v new file mode 100644 index 0000000..2afacac --- /dev/null +++ b/rtl/util/commoncell/src/Queue/dv/AgeMatrixSelector_tb.v @@ -0,0 +1,168 @@ +// module AgeMatrixSelector_tb; + +// // Parameters +// localparam int unsigned EntryCount = 4; +// localparam int unsigned EnqWidth = 2; +// localparam int unsigned SelWidth = 2; + +// // Ports +// reg [EnqWidth-1:0] enq_fire_i; +// reg [EntryCount-1:0] enq_mask_i[EnqWidth]; +// reg [EntryCount-1:0] sel_mask_i; +// wire [EntryCount-1:0] result_mask_o[SelWidth]; +// reg [EntryCount-1:0] entry_vld_i; +// reg clk = 0; +// reg rstn = 0; + +// bit [EntryCount-1:0][EntryCount-1:0] golden_age_matrix_d, golden_age_matrix_q; +// bit [EntryCount-1:0][EntryCount-1:0] golden_masked_age_matrix[SelWidth]; +// bit [EntryCount-1:0] golden_selected_mask[SelWidth]; +// bit [EntryCount-1:0] golden_sel_result[SelWidth]; + +// always @(posedge clk) begin +// if (~rstn) begin +// golden_age_matrix_q <= 0; +// end else begin +// for (int row = 0; row < EntryCount; row++) begin +// for (int col = 0; col < EntryCount; col++) begin +// if (row != col) begin +// golden_age_matrix_q[row][col] <= golden_age_matrix_d[row][col]; +// end +// end +// end +// end +// end +// always @(*) begin +// for (int row = 0; row < EntryCount; row++) begin +// golden_age_matrix_q[row][row] <= entry_vld_i[row]; +// end +// end +// always @(*) begin +// golden_age_matrix_d = golden_age_matrix_q; +// for (int i = 0; i < EnqWidth; i++) begin +// if (enq_fire_i[i]) begin +// for (int row = 0; row < EntryCount; row++) begin +// if (enq_mask_i[i][row]) begin +// for (int col = 0; col < EntryCount; col++) begin +// golden_age_matrix_d[row][col] = ~golden_age_matrix_d[col][col]; +// end +// end +// end +// end +// end +// end + +// always @(*) begin +// for (int i = 0; i < EntryCount; i++) begin +// if (i == 0) begin +// golden_selected_mask[i] = 0; +// end else begin +// golden_selected_mask[i] = golden_selected_mask[i-1] | golden_sel_result[i-1]; +// end +// end +// end + +// always @(*) begin +// for (int i = 0; i < SelWidth; i++) begin +// golden_masked_age_matrix[i] = golden_age_matrix_q; +// for (int col = 0; col < EntryCount; col++) begin +// if (golden_selected_mask[i][col] | ~sel_mask_i[col]) begin +// for (int row = 0; row < EntryCount; row++) begin +// golden_masked_age_matrix[i][row][col] = col != row; +// end +// end +// end +// end +// end + +// always @(*) begin +// for (int i = 0; i < SelWidth; i++) begin +// for (int row = 0; row < EntryCount; row++) begin +// golden_sel_result[i][row] = &golden_masked_age_matrix[i][row]; +// end +// end +// end + +// default disable iff (~rstn); + +// int iter = 10000; + +// int random_ptr; + +// CHECK_EQULATION : +// assert property (@(negedge clk) golden_age_matrix_q == AgeMatrixSelector_dut.age_matrix_q) +// else begin +// $fatal("\n Error : Age matrix is not equal to golden one \n"); +// end + +// initial begin +// #100 rstn = 1'b1; +// entry_vld_i = 0; +// enq_fire_i = 0; +// sel_mask_i = 0; + +// repeat (iter) begin +// @(posedge clk); +// entry_vld_i = entry_vld_i & ~(entry_vld_i & $urandom_range(0, (1 << EntryCount) - 1)); +// for (int i = 0; i < EnqWidth; i++) begin +// if (enq_fire_i[i]) begin +// entry_vld_i = entry_vld_i | enq_mask_i[i]; +// end +// end +// sel_mask_i = $urandom_range(0, (1 << EntryCount) - 1) & entry_vld_i; + +// @(negedge clk); +// for (int i = 0; i < EnqWidth; i++) begin +// enq_fire_i[i] = 0; +// enq_mask_i[i] = 0; +// random_ptr = $urandom_range(0, EntryCount - 1); +// if (~entry_vld_i[random_ptr]) begin +// enq_fire_i[i] = 1; +// enq_mask_i[i][random_ptr] = 1; +// end +// for(int j = 0 ; j < i; j++) begin +// if(enq_fire_i[j] && (enq_mask_i[j] == enq_mask_i[i])) begin +// enq_fire_i[i] = 0; +// end +// end +// end +// for (int i = 0; i < SelWidth; i++) begin +// assert (result_mask_o[i] == golden_sel_result[i]) +// else begin +// $fatal("\n Error : Selection is not equal, golden[%b] our[%b] \n", golden_sel_result[i], +// result_mask_o[i]); +// end +// end +// end +// $info("\n PASS after %d iter \n",iter); +// $finish(); +// end + + +// AgeMatrixSelector #( +// .EntryCount(EntryCount), +// .EnqWidth (EnqWidth), +// .SelWidth (SelWidth) +// ) AgeMatrixSelector_dut ( +// .enq_fire_i(enq_fire_i), +// .enq_mask_i(enq_mask_i), +// .sel_mask_i(sel_mask_i), +// .result_mask_o(result_mask_o), +// .entry_vld_i(entry_vld_i), +// .clk(clk), +// .rstn(rstn) +// ); + +// `ifdef DUMPON +// initial begin : GEN_WAVEFORM +// $fsdbDumpfile("AgeMatrixSelector_tb.fsdb"); +// $fsdbDumpvars(0, AgeMatrixSelector_tb); +// $fsdbDumpvars("+mda"); +// $fsdbDumpvars("+all"); +// $fsdbDumpon(); +// end +// `endif + +// always #20 clk = !clk; + +// endmodule diff --git a/rtl/util/commoncell/src/Queue/dv/FIFO/MultiPortStreamFIFO_tb.v b/rtl/util/commoncell/src/Queue/dv/FIFO/MultiPortStreamFIFO_tb.v new file mode 100644 index 0000000..f07255f --- /dev/null +++ b/rtl/util/commoncell/src/Queue/dv/FIFO/MultiPortStreamFIFO_tb.v @@ -0,0 +1,111 @@ +module MultiPortStreamFIFO_tb; + + // Parameters + localparam int unsigned Depth = 8; + localparam int unsigned DataWidth = 32; + localparam int unsigned EnqWidth = 2; + localparam int unsigned DeqWidth = 2; + localparam int unsigned TakenAll = 0; + + // Ports + reg [EnqWidth-1:0] enq_vld_i; + reg [EnqWidth-1:0][DataWidth-1:0] enq_payload_i; + wire [EnqWidth-1:0] enq_rdy_o; + wire [DeqWidth-1:0] deq_vld_o; + wire [DeqWidth-1:0][DataWidth-1:0] deq_payload_o; + reg [DeqWidth-1:0] deq_rdy_i; + reg flush_i = 0; + reg clk = 0; + reg rstn = 0; + + bit [DataWidth-1:0] golden_fifo[$]; + bit [DataWidth-1:0] golden_fifo_front; + int usage = 0; + + int iter = 1000000; + + always @(posedge clk) begin + for (int i = 0; i < DeqWidth; i++) begin : test_deq + if (deq_vld_o[i] & deq_rdy_i[i]) begin + usage = usage - 1; + golden_fifo_front = golden_fifo.pop_front(); + assert (deq_payload_o[i] == golden_fifo_front) + else begin + $fatal("\n Error: Fail when check equalation, ours[%x] -- gloden[%x]", deq_payload_o[i], + golden_fifo_front); + end + end + end + for (int i = 0; i < EnqWidth; i++) begin : enq_test + if (enq_vld_i[i] & enq_rdy_o[i]) begin + usage = usage + 1; + golden_fifo.push_back(enq_payload_i[i]); + end + end + end + + initial begin + #10 rstn = 1'b1; + repeat (iter) begin : random_test + @(negedge clk); + deq_rdy_i = {DeqWidth{1'b0}}; + enq_vld_i = {EnqWidth{1'b0}}; + for (int i = 0; i < DeqWidth; i++) begin : gen_deq_rdy + if ($urandom_range(0, 1)) begin + deq_rdy_i[i] = 1'b1; + end else begin + break; + end + end + for (int i = 0; i < EnqWidth; i++) begin : gen_enq_vld + if ($urandom_range(0, 1)) begin + enq_vld_i[i] = 1'b1; + enq_payload_i[i] = $urandom(); + end else begin + break; + end + end + CHECK_USAGE : + assert (usage == MultiPortStreamFIFO_dut.u_QueueManager.usage) + else begin + $fatal("\n Error : Usage is not equal, which should never happen! ours[%d] gloden[%d] \n", + MultiPortStreamFIFO_dut.u_QueueManager.usage, usage); + end + end + $info("\n PASS after %d iter \n", iter); + $finish; + end + + + MultiPortStreamFIFO #( + .Depth(Depth), + .DataWidth(DataWidth), + .EnqWidth(EnqWidth), + .DeqWidth(DeqWidth), + .TakenAll(TakenAll) + ) MultiPortStreamFIFO_dut ( + .enq_vld_i(enq_vld_i), + .enq_payload_i(enq_payload_i), + .enq_rdy_o(enq_rdy_o), + .deq_vld_o(deq_vld_o), + .deq_payload_o(deq_payload_o), + .deq_rdy_i(deq_rdy_i), + .flush_i(flush_i), + .clk(clk), + .rstn(rstn) + ); + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("MultiPortStreamFIFO_tb.fsdb"); + $fsdbDumpvars(0, MultiPortStreamFIFO_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + + always #20 clk = !clk; + + +endmodule diff --git a/rtl/util/commoncell/src/Queue/dv/StaticPrioritySelector_tb.v b/rtl/util/commoncell/src/Queue/dv/StaticPrioritySelector_tb.v new file mode 100644 index 0000000..a711d0b --- /dev/null +++ b/rtl/util/commoncell/src/Queue/dv/StaticPrioritySelector_tb.v @@ -0,0 +1,99 @@ + +module StaticPrioritySelector_tb; + + // Parameters + localparam int unsigned Depth = 8; + localparam int unsigned EnqWidth = 2; + localparam int unsigned SelWidth = 2; + localparam int unsigned PtrWidth = $clog2(Depth); + + // Ports + wire [EnqWidth-1:0][Depth-1:0] enq_mask_o; + reg [Depth-1:0] sel_mask_i; + wire [SelWidth-1:0][Depth-1:0] result_mask_o; + reg [Depth-1:0] entry_vld_i; + + int iter = 100000; + + bit [EnqWidth-1:0][Depth-1:0] golden_enq_mask; + bit [SelWidth-1:0][Depth-1:0] golden_result_mask; + + int cnt; + + initial begin + repeat (iter) begin + entry_vld_i = $urandom_range(0, ((1 << Depth) - 1)); + sel_mask_i = $urandom_range(0, ((1 << Depth) - 1)) & entry_vld_i; + #100; + for (int i = 0; i < EnqWidth; i++) begin : init_golden_enq_mask + golden_enq_mask[i] = 0; + end + cnt = 0; + for (int i = 0; i < Depth; i++) begin : gen_golden_enq_mask + if (~entry_vld_i[i]) begin + golden_enq_mask[cnt][i] = 1'b1; + cnt = cnt + 1; + if (cnt == EnqWidth) begin + break; + end + end + end + for (int i = 0; i < SelWidth; i++) begin : init_golden_result_mask + golden_result_mask[i] = 0; + end + cnt = 0; + for (int i = 0; i < Depth; i++) begin : gen_golden_result_mask + if (sel_mask_i[i]) begin + golden_result_mask[cnt][i] = 1'b1; + cnt = cnt + 1; + if (cnt == SelWidth) begin + break; + end + end + end + CheckEnqEqual : + for (int i = 0; i < EnqWidth; i++) begin + assert (golden_enq_mask[i] == enq_mask_o[i]) + else begin + $fatal("\n Masks are not equal, ours[%b] - golden[%b] \n", enq_mask_o[i], + golden_enq_mask[i]); + end + end + for (int i = 0; i < SelWidth; i++) begin + CheckSelEqual : + assert (golden_result_mask[i] == result_mask_o[i]) + else begin + $fatal("\n Masks are not equal, ours[%b] - golden[%b] \n", result_mask_o[i], + golden_result_mask[i]); + end + end + end + #100; + $info("PASS\n"); + $finish; + end + + + StaticPrioritySelector #( + .Depth(Depth), + .EnqWidth(EnqWidth), + .SelWidth(SelWidth) + ) StaticPrioritySelector_dut ( + .enq_mask_o(enq_mask_o), + .sel_mask_i(sel_mask_i), + .result_mask_o(result_mask_o), + .entry_vld_i(entry_vld_i) + ); + + +`ifdef DUMPON + initial begin : GEN_WAVEFORM + $fsdbDumpfile("StaticPrioritySelector_tb.fsdb"); + $fsdbDumpvars(0, StaticPrioritySelector_tb); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon(); + end +`endif + +endmodule diff --git a/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v b/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v new file mode 100644 index 0000000..20c5434 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v @@ -0,0 +1,187 @@ +module AgeMatrixSelector #( + parameter int unsigned EntryCount = 4, + parameter int unsigned EnqWidth = 2, + parameter int unsigned SelWidth = 2 +) ( + input wire [EnqWidth-1:0] enq_fire_i, + input wire [EnqWidth-1:0][EntryCount-1:0] enq_mask_i, + input wire deq_fire_i, + input wire [EntryCount-1:0] deq_mask_i, + input wire [EntryCount-1:0] sel_mask_i, + output wire [SelWidth-1:0][EntryCount-1:0] result_mask_o, + input wire [EntryCount-1:0] entry_vld_i, + input wire clk, + input wire rstn +); + + wire [EntryCount-1:0] age_matrix_clk_en; + wire [EntryCount-1:0] enq_age_matrix_en; + wire [EntryCount-1:0] deq_age_matrix_en; + wire [EnqWidth-1:0][EntryCount-1:0] enq_dependency_vec; + wire [EntryCount-1:0][EnqWidth-1:0] enq_entry_sel_mask; + wire [EntryCount-1:0][EntryCount-1:0] enq_age_matrix; + wire [EntryCount-1:0][EntryCount-1:0] deq_age_matrix; + reg [EntryCount-1:0][EntryCount-1:0] age_matrix_d, age_matrix_q; + + wire [SelWidth-1:0][EntryCount-1:0] selected_vec; + wire [SelWidth-1:0][EntryCount-1:0][EntryCount-1:0] masked_age_matrix; + + // In age order select + generate + for (genvar i = 0; i < SelWidth; i++) begin : gen_selected_vec + if (i == 0) begin : gen_initial_one + assign selected_vec[i] = {EntryCount{1'b0}}; + end else begin : gen_others + assign selected_vec[i] = selected_vec[i-1] | result_mask_o[i-1]; + end + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_sel_matrix + for (genvar col = 0; col < EntryCount; col++) begin : gen_col + for (genvar row = 0; row < EntryCount; row++) begin : gen_row + if (col == row) begin : gen_masked_vld + assign masked_age_matrix[i][row][col] = sel_mask_i[col] & ~selected_vec[i][col]; + end else begin : gen_masked_dependency + assign masked_age_matrix[i][row][col] = (sel_mask_i[col] & ~selected_vec[i][col]) ? + age_matrix_q[row][col] : 1'b1; + end + end + end + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_multi_result + for (genvar row = 0; row < EntryCount; row++) begin : gen_one + assign result_mask_o[i][row] = &masked_age_matrix[i][row]; + end + end + endgenerate + + + // Enq -> Set dependency bits + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_enq_entry_en + assign enq_age_matrix_en[i] = |enq_entry_sel_mask[i]; + end + endgenerate + + generate + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_dependency_vec + if (i == 0) begin : gen_init_vec + assign enq_dependency_vec[i] = deq_fire_i ? (~entry_vld_i | deq_mask_i) : ~entry_vld_i; + end else begin : gen_vec_with_inter_check + assign enq_dependency_vec[i] = enq_fire_i[i-1] ? + (enq_dependency_vec[i-1] & ~enq_mask_i[i-1]) : enq_dependency_vec[i-1]; + end + end + endgenerate + + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_enq_entry_sel_mask + for (genvar j = 0; j < EnqWidth; j++) begin : gen_sel + assign enq_entry_sel_mask[i][j] = enq_fire_i[j] & enq_mask_i[j][i]; + end + end + endgenerate + + // Deq -> Clear dependency bits + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_deq_entry_clk_en + assign deq_age_matrix[i] = age_matrix_q[i] | deq_mask_i; + end + endgenerate + + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_deq_entry_en + assign deq_age_matrix_en[i] = deq_fire_i & entry_vld_i[i]; + end + endgenerate + + assign age_matrix_clk_en = enq_age_matrix_en | deq_age_matrix_en; + + // Age matrix update + + always @(*) begin : age_matrix_vld_dff + for (int i = 0; i < EntryCount; i++) begin + age_matrix_q[i][i] = entry_vld_i[i]; + end + end + + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_age_matrix_update_logic + MuxOH #( + .InputWidth(2), + .DataWidth (EntryCount) + ) u_age_matrix_update_MuxOH ( + .sel_i ({enq_age_matrix_en[i], deq_age_matrix_en[i]}), + .data_i({enq_age_matrix[i], deq_age_matrix[i]}), + .data_o(age_matrix_d[i]) + ); + end + endgenerate + + + generate + for (genvar i = 0; i < EntryCount; i++) begin : gen_entry_update_dependency_vec + MuxOH #( + .InputWidth(EnqWidth), + .DataWidth (EntryCount) + ) u_depend_vec_MuxOH ( + .sel_i (enq_entry_sel_mask[i]), + .data_i(enq_dependency_vec), + .data_o(enq_age_matrix[i]) + ); + end + endgenerate + + always @(posedge clk or negedge rstn) begin : age_matrix_dependency_dff + if (~rstn) begin + for (int row = 0; row < EntryCount; row++) begin + for (int col = 0; col < EntryCount; col++) begin + if (row != col) begin + age_matrix_q[row][col] <= 1'b0; + end + end + end + end else begin + for (int row = 0; row < EntryCount; row++) begin + if (age_matrix_clk_en[row]) begin + for (int col = 0; col < EntryCount; col++) begin + if (row != col) begin + age_matrix_q[row][col] <= age_matrix_d[row][col]; + end + end + end + end + end + end + +`ifndef SYNTHESIS + default disable iff (~rstn); + generate + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_checker + ENQ_VLD_ENTRY : + assert property (@(posedge clk) enq_fire_i[i] |-> ((enq_mask_i[i] & entry_vld_i) == 0)) + else begin + $fatal("\n Error : Enqueueing a valid entry! enq_mask[%b]\n", enq_mask_i[i]); + end + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_sel_checker + SEL_INVLD_ENTRY : + assert property (@(negedge clk) |sel_mask_i |-> ((result_mask_o[i] & ~entry_vld_i) == 0)) + else begin + $fatal("\n Error : Selecting a invalid entry! sel_mask[%b]\n", + (result_mask_o[i] & ~entry_vld_i)); + end + + RESULT_MASK_IS_ONEHOT : + assert property (@(negedge clk) |sel_mask_i |-> $onehot0(result_mask_o[i])) + else begin + $fatal("\n Error : Got multi-choice which should never happend! result_mask[%b]\n", + result_mask_o[i]); + end + end + endgenerate +`endif + + +endmodule + + diff --git a/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithAgeMatrix.v b/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithAgeMatrix.v new file mode 100644 index 0000000..2df9354 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithAgeMatrix.v @@ -0,0 +1,61 @@ +// module FIAOWithAgeMatrix #( +// parameter int unsigned Depth = 8, +// parameter int unsigned EnqWidth = 2, +// parameter int unsigned SelWidth = 2, +// localparam int unsigned PtrWidth = $clog2(Depth) +// ) ( +// // Enqueue Port +// input wire [EnqWidth-1:0] enq_fire_i, +// input wire [EnqWidth-1:0] enq_eval_i, +// output wire [Depth-1:0] enq_mask_o[EnqWidth], +// output wire [PtrWidth-1:0] enq_ptr_o[EnqWidth], +// // Select Port +// input wire [Depth-1:0] sel_mask_i, +// output wire [Depth-1:0] result_mask_o[SelWidth], +// // Status +// input wire [Depth-1:0] entry_vld_i, + +// input wire clk, +// input wire rstn +// ); + +// wire [Depth-1:0] enq_rdy_mask[EnqWidth]; + +// generate +// for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_rdy_mask +// if (i == 0) begin : gen_initial_one +// assign enq_rdy_mask[i] = ~entry_vld_i; +// end else begin : gen_others +// assign enq_rdy_mask[i] = enq_eval_i[i-1] ? +// enq_rdy_mask[i-1] : (enq_rdy_mask[i-1] & ~enq_mask_o[i-1]); +// end +// end +// for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_mask +// assign enq_mask_o[i] = enq_rdy_mask[i] & ~(enq_rdy_mask[i] - 1'b1); +// OH2UInt #( +// .InputWidth(Depth) +// ) u_OH2UInt ( +// .oh_i(enq_mask_o[i]), +// .result_o(enq_ptr_o[i]) +// ); + +// end +// endgenerate + +// AgeMatrixSelector #( +// .EntryCount(Depth), +// .EnqWidth (EnqWidth), +// .SelWidth (SelWidth) +// ) u_AgeMatrixSelector ( +// .enq_fire_i(enq_fire_i), +// .enq_mask_i(enq_mask_o), +// .sel_mask_i(sel_mask_i), +// .result_mask_o(result_mask_o), +// .entry_vld_i(entry_vld_i), +// .clk(clk), +// .rstn(rstn) +// ); + + + +// endmodule diff --git a/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithQueueManager.v b/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithQueueManager.v new file mode 100644 index 0000000..760aa99 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/FIAO/FIAOWithQueueManager.v @@ -0,0 +1,126 @@ +module FIAOWithQueueManager #( + parameter int unsigned Depth = 8, + parameter int unsigned EnqWidth = 2, + parameter int unsigned DeqWidth = 2, + parameter int unsigned SelWidth = 2, + localparam int unsigned PtrWidth = $clog2(Depth) +) ( + // Enqueue Port + input wire [EnqWidth-1:0] enq_fire_i, + input wire [EnqWidth-1:0] enq_eval_i, + output wire [EnqWidth-1:0][Depth-1:0] enq_mask_o, + output wire [EnqWidth-1:0][PtrWidth-1:0] enq_ptr_o, + // Select Port + input wire [Depth-1:0] sel_mask_i, + output wire [SelWidth-1:0][Depth-1:0] result_mask_o, + // Status + input wire [Depth-1:0] entry_vld_i, + + input wire clk, + input wire rstn +); + + localparam int unsigned TagWidth = PtrWidth + 1; + + wire [EnqWidth-1:0][PtrWidth-1:0] enq_ptr; + wire [EnqWidth-1:0] enq_flag; + wire [EnqWidth-1:0][TagWidth-1:0] enq_tag; + wire [DeqWidth-1:0] enq_eval; + wire [DeqWidth-1:0] enq_fire; + + wire [DeqWidth-1:0][PtrWidth-1:0] deq_ptr; + wire [DeqWidth-1:0] deq_flag; + wire [DeqWidth-1:0][TagWidth-1:0] deq_tag; + wire [DeqWidth-1:0] deq_eval; + wire [DeqWidth-1:0] deq_fire; + + wire first_enq_flag; + wire [PtrWidth-1:0] first_enq_ptr; + wire first_deq_flag; + wire [PtrWidth-1:0] first_deq_ptr; + wire flag_equal; + wire [Depth-1:0] head_inv_mask; + wire [Depth-1:0] tail_inv_mask; + wire [Depth-1:0] head_side_sel_mask[SelWidth-1:0]; + wire [Depth-1:0] tail_side_sel_mask[SelWidth-1:0]; + wire [Depth-1:0] head_side_sel_result[SelWidth-1:0]; + wire [Depth-1:0] tail_side_sel_result[SelWidth-1:0]; + wire [Depth-1:0] selected_mask[SelWidth-1:0]; + + // Enqueue Logic + assign enq_eval = enq_eval_i; + assign enq_fire = enq_fire_i; + assign enq_ptr_o = enq_ptr; + generate + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_logic + assign enq_ptr[i] = enq_tag[i][PtrWidth-1:0]; + assign enq_flag[i] = enq_tag[i][TagWidth-1]; + assign enq_mask_o[i] = {{Depth - 1{1'b0}}, 1'b1} << enq_ptr[i]; + end + endgenerate + + + // Select Logic + assign first_enq_flag = enq_flag[0]; + assign first_enq_ptr = enq_ptr[0]; + assign first_deq_flag = deq_flag[0]; + assign first_deq_ptr = deq_ptr[0]; + assign flag_equal = ~(first_enq_flag ^ first_deq_flag); + + assign head_inv_mask = (({{PtrWidth - 1{1'b0}}, {1'b1}} << first_deq_ptr) - 1'b1); + assign tail_inv_mask = ~(({{PtrWidth - 1{1'b0}}, {1'b1}} << first_enq_ptr) - 1'b1); + + generate + for (genvar i = 0; i < SelWidth; i++) begin : gen_selected_mask + if (i == 0) begin : gen_initial_one + assign selected_mask[i] = {Depth{1'b0}}; + end else begin : gen_others + assign selected_mask[i] = selected_mask[i-1] | result_mask_o[i-1]; + end + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_side_sel_mask + assign head_side_sel_mask[i] = sel_mask_i & ~selected_mask[i] & ~head_inv_mask; + assign tail_side_sel_mask[i] = sel_mask_i & ~selected_mask[i] & ~tail_inv_mask; + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_priority_select_result + assign head_side_sel_result[i] = head_side_sel_mask[i] & ~(head_side_sel_mask[i] - 1'b1); + assign tail_side_sel_result[i] = tail_side_sel_mask[i] & ~(tail_side_sel_mask[i] - 1'b1); + assign result_mask_o[i] = flag_equal ? head_side_sel_result[i] : + (|head_side_sel_result[i] ? head_side_sel_result[i] : tail_side_sel_result[i]); + + end + endgenerate + + // Dequeue Logic + generate + for (genvar i = 0; i < DeqWidth; i++) begin : gen_deq_logic + assign deq_ptr[i] = deq_tag[i][PtrWidth-1:0]; + assign deq_flag[i] = deq_tag[i][TagWidth-1]; + assign deq_eval[i] = 1'b1; + assign deq_fire[i] = ~entry_vld_i[deq_tag[i]]; + end + endgenerate + + QueueManager #( + .Depth(Depth), + .EnqWidth(EnqWidth), + .DeqWidth(DeqWidth), + .FlagEnable(1), + .EnqCollapse(1), + .DeqCollapse(0), + .InitFull(0) + ) u_QueueManager ( + .enq_fire_i(enq_fire), + .enq_eval_i(enq_eval), + .enq_tag_o(enq_tag), + .deq_fire_i(deq_fire), + .deq_eval_i(deq_eval), + .deq_tag_o(deq_tag), + .flush_i(1'b0), + .clk(clk), + .rstn(rstn) + ); + + + +endmodule diff --git a/rtl/util/commoncell/src/Queue/hw/FIFO/MultiPortStreamFIFO.v b/rtl/util/commoncell/src/Queue/hw/FIFO/MultiPortStreamFIFO.v new file mode 100644 index 0000000..c0f9d28 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/FIFO/MultiPortStreamFIFO.v @@ -0,0 +1,124 @@ +module MultiPortStreamFIFO #( + parameter int unsigned Depth = 8, + parameter int unsigned DataWidth = 32, + parameter int unsigned EnqWidth = 2, + parameter int unsigned DeqWidth = 2, + parameter int unsigned TakenAll = 0 +) ( + input wire [EnqWidth-1:0] enq_vld_i, + input wire [EnqWidth-1:0][DataWidth-1:0] enq_payload_i, + output wire [EnqWidth-1:0] enq_rdy_o, + output wire [DeqWidth-1:0] deq_vld_o, + output wire [DeqWidth-1:0][DataWidth-1:0] deq_payload_o, + input wire [DeqWidth-1:0] deq_rdy_i, + input wire flush_i, + input wire clk, + input wire rstn +); + + localparam int unsigned PtrWidth = $clog2(Depth); + + reg [Depth-1:0] payload_clk_en; + reg [Depth-1:0] vld_dff_d, vld_dff_q; + reg [Depth-1:0][DataWidth-1:0] payload_dff_d, payload_dff_q; + + wire [EnqWidth-1:0][PtrWidth-1:0] enq_ptr; + wire [DeqWidth-1:0][PtrWidth-1:0] deq_ptr; + + wire [EnqWidth-1:0] enq_rdy; + wire [EnqWidth-1:0] enq_fire; + wire [DeqWidth-1:0] deq_fire; + + assign enq_fire = enq_vld_i & enq_rdy_o; + assign deq_fire = deq_vld_o & deq_rdy_i; + + always @(*) begin : vld_dff_update_logic + vld_dff_d = vld_dff_q; + for (int i = 0; i < EnqWidth; i++) begin + if (enq_fire[i]) begin + vld_dff_d[enq_ptr[i]] = 1'b1; + end + end + for (int i = 0; i < DeqWidth; i++) begin + if (deq_fire[i]) begin + vld_dff_d[deq_ptr[i]] = 1'b0; + end + end + if (flush_i) begin + vld_dff_d = {Depth{1'b0}}; + end + end + + always @(*) begin : payload_update_logic + for (int i = 0; i < Depth; i++) begin : payload_init + payload_clk_en[i] = 1'b0; + payload_dff_d[i] = payload_dff_q[i]; + end + for (int i = 0; i < EnqWidth; i++) begin : enq_update + if (enq_fire[i] & ~flush_i) begin + payload_clk_en[enq_ptr[i]] = 1'b1; + payload_dff_d[enq_ptr[i]] = enq_payload_i[i]; + end + end + end + + generate + if (TakenAll) begin : gen_rdy_if_need_taken_all + assign enq_rdy_o = {EnqWidth{&enq_rdy}}; + end else begin : gen_normal_rdy + assign enq_rdy_o = enq_rdy; + end + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_rdy_logic + assign enq_rdy[i] = ~vld_dff_q[enq_ptr[i]]; + end + for (genvar i = 0; i < DeqWidth; i++) begin : gen_deq_logic + assign deq_vld_o[i] = vld_dff_q[deq_ptr[i]]; + assign deq_payload_o[i] = payload_dff_q[deq_ptr[i]]; + end + endgenerate + + + DFFR #( + .Width(Depth) + ) u_vld_dff ( + .CLK(clk), + .RSTN(rstn), + .DRST({Depth{1'b0}}), + .D(vld_dff_d), + .Q(vld_dff_q) + ); + + generate + for (genvar i = 0; i < Depth; i++) begin : gen_payload_dff + DFFE #( + .Width(DataWidth) + ) u_payload_dff ( + .CLK(clk), + .EN (payload_clk_en[i]), + .D (payload_dff_d[i]), + .Q (payload_dff_q[i]) + ); + end + endgenerate + + QueueManager #( + .Depth(Depth), + .EnqWidth(EnqWidth), + .DeqWidth(DeqWidth), + .FlagEnable(0), + .EnqCollapse(1), + .DeqCollapse(0), + .InitFull(0) + ) u_QueueManager ( + .enq_fire_i(enq_fire), + .enq_eval_i(enq_vld_i), + .enq_tag_o(enq_ptr), + .deq_fire_i(deq_fire), + .deq_eval_i(deq_vld_o), + .deq_tag_o(deq_ptr), + .flush_i(flush_i), + .clk(clk), + .rstn(rstn) + ); + +endmodule diff --git a/rtl/util/commoncell/src/Queue/hw/QueueManager.v b/rtl/util/commoncell/src/Queue/hw/QueueManager.v new file mode 100644 index 0000000..adb6497 --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/QueueManager.v @@ -0,0 +1,206 @@ +module QueueManager #( + parameter int unsigned Depth = 8, + parameter int unsigned EnqWidth = 2, + parameter int unsigned DeqWidth = 2, + parameter bit FlagEnable = 0, + parameter bit EnqCollapse = 0, + parameter bit DeqCollapse = 0, + parameter bit InitFull = 0, + localparam int unsigned PtrWidth = $clog2(Depth), + localparam int unsigned CntWidth = $clog2(Depth + 1), + localparam int unsigned TagWidth = PtrWidth + FlagEnable +) ( + input wire [EnqWidth-1:0] enq_fire_i, + input wire [EnqWidth-1:0] enq_eval_i, + output wire [EnqWidth-1:0][TagWidth-1:0] enq_tag_o, + input wire [DeqWidth-1:0] deq_fire_i, + input wire [DeqWidth-1:0] deq_eval_i, + output wire [DeqWidth-1:0][TagWidth-1:0] deq_tag_o, + input wire flush_i, + input wire clk, + input wire rstn +); + + localparam int unsigned EnqCountWidth = $clog2(EnqWidth + 1); + localparam int unsigned DeqCountWidth = $clog2(DeqWidth + 1); + + function automatic [TagWidth-1:0] tagPlusOne; + input [TagWidth-1:0] originalTag; + begin + if (FlagEnable) begin + if (originalTag[PtrWidth-1:0] == Depth - 1) begin + tagPlusOne = {~originalTag[TagWidth-1], {PtrWidth{1'b0}}}; + end else begin + tagPlusOne = originalTag + 1'b1; + end + end else begin + if (originalTag[PtrWidth-1:0] == Depth - 1) begin + tagPlusOne = {PtrWidth{1'b0}}; + end else begin + tagPlusOne = originalTag + 1'b1; + end + end + end + endfunction + + function automatic [TagWidth-1:0] tagPlus; + input [TagWidth-1:0] originalTag; + input [PtrWidth-1:0] delta; + reg [PtrWidth:0] sum; + reg [PtrWidth:0] reverse_sum; + begin + if (2 ** $clog2(Depth) == Depth) begin + tagPlus = originalTag + delta; + end else begin + sum = $unsigned({1'b0, originalTag[PtrWidth-1:0]}) + $unsigned({{1'b0}, delta}); + reverse_sum = $signed(sum) - $unsigned(Depth); + if ($signed(reverse_sum) >= 0) begin + if (FlagEnable) begin + tagPlus = {~originalTag[TagWidth-1], reverse_sum[PtrWidth-1:0]}; + end else begin + tagPlus = reverse_sum[PtrWidth-1:0]; + end + end else begin + if (FlagEnable) begin + tagPlus = {originalTag[TagWidth-1], sum[PtrWidth-1:0]}; + end else begin + tagPlus = sum[PtrWidth-1:0]; + end + end + end + end + endfunction + + wire enq_tag_clk_en; + wire [EnqWidth-1:0] enq_rdy_mask, enq_collapse_rdy_mask; + wire [EnqCountWidth-1:0] enq_cnt; + wire [PtrWidth-1:0] enq_delta; + reg [TagWidth-1:0] enq_tag_d, enq_tag_q; + + wire deq_tag_clk_en; + wire [DeqCountWidth-1:0] deq_cnt; + wire [PtrWidth-1:0] deq_delta; + reg [TagWidth-1:0] deq_tag_d, deq_tag_q; + + assign enq_tag_clk_en = (|enq_fire_i); + assign deq_tag_clk_en = (|deq_fire_i); + + assign enq_delta = {{PtrWidth - EnqCountWidth{1'b0}}, enq_cnt}; + assign deq_delta = {{PtrWidth - DeqCountWidth{1'b0}}, deq_cnt}; + + + generate + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_ptr + if (i == 0) begin : gen_first_enq_ptr + assign enq_tag_o[i] = enq_tag_q; + end else begin : gen_next_enq_ptr + assign enq_tag_o[i] = (EnqCollapse & ~enq_eval_i[i-1]) ? enq_tag_o[i-1] : tagPlusOne( + enq_tag_o[i-1] + ); + end + end + for (genvar i = 0; i < DeqWidth; i++) begin : gen_deq_ptr + if (i == 0) begin : gen_first_deq_ptr + assign deq_tag_o[i] = deq_tag_q; + end else begin : gen_next_deq_ptr + assign deq_tag_o[i] = (DeqCollapse & ~deq_eval_i[i-1]) ? deq_tag_o[i-1] : tagPlusOne( + deq_tag_o[i-1] + ); + end + end + endgenerate + + always @(*) begin : enq_tag_update_logic + enq_tag_d = enq_tag_q; + if (enq_tag_clk_en) begin + enq_tag_d = tagPlus(enq_tag_q, enq_delta); + end + if (flush_i) begin + if (InitFull & FlagEnable) begin + enq_tag_d = {~deq_tag_q[TagWidth-1], deq_tag_q[PtrWidth-1:0]}; + end else begin + enq_tag_d = deq_tag_q; + end + end + end + + always @(*) begin : deq_tag_update_logic + deq_tag_d = deq_tag_q; + if (deq_tag_clk_en) begin + deq_tag_d = tagPlus(deq_tag_q, deq_delta); + end + if (flush_i) begin + deq_tag_d = deq_tag_q; + end + end + + CountOne #( + .InputWidth(EnqWidth) + ) u_enqCounter ( + .bits_i(enq_fire_i), + .cnt_o (enq_cnt) + ); + CountOne #( + .InputWidth(DeqWidth) + ) u_deqCounter ( + .bits_i(deq_fire_i), + .cnt_o (deq_cnt) + ); + + DFFRE #( + .Width(TagWidth) + ) u_enq_tag_dff ( + .CLK(clk), + .RSTN(rstn), + .EN(enq_tag_clk_en | flush_i), + .DRST({(InitFull & FlagEnable), {TagWidth - 1{1'b0}}}), + .D(enq_tag_d), + .Q(enq_tag_q) + ); + + DFFRE #( + .Width(TagWidth) + ) u_deq_tag_dff ( + .CLK(clk), + .RSTN(rstn), + .EN(deq_tag_clk_en | flush_i), + .DRST({TagWidth{1'b0}}), + .D(deq_tag_d), + .Q(deq_tag_q) + ); + +`ifndef SYNTHESIS + default disable iff (~rstn); + + int unsigned usage = 0; + always @(posedge clk) begin : gen_usage + if (~rstn) begin + usage = 0; + end else begin + if (flush_i) begin + usage = 0; + end else if (enq_tag_clk_en & deq_tag_clk_en) begin + usage = usage + enq_cnt - deq_cnt; + end else if (enq_tag_clk_en) begin + usage = usage + enq_cnt; + end else if (deq_tag_clk_en) begin + usage = usage - deq_cnt; + end + end + end + + CHECK_ENQ_WHEN_FULL : + assert property (@(posedge clk) enq_tag_clk_en |-> (Depth - usage >= enq_cnt)) + else begin + $fatal("\n Error : Enqueue count[%d] > Availible count[%d]\n", enq_cnt, Depth - usage); + end + + CHECK_DEQ_WHEN_EMPTY : + assert property (@(posedge clk) deq_tag_clk_en |-> (usage >= deq_cnt)) + else begin + $fatal("\n Error : Dequeue count[%d] > Availible count[%d]\n", deq_cnt, usage); + end + +`endif + +endmodule diff --git a/rtl/util/commoncell/src/Queue/hw/StaticPrioritySelector.v b/rtl/util/commoncell/src/Queue/hw/StaticPrioritySelector.v new file mode 100644 index 0000000..6cea81f --- /dev/null +++ b/rtl/util/commoncell/src/Queue/hw/StaticPrioritySelector.v @@ -0,0 +1,52 @@ +module StaticPrioritySelector #( + parameter int unsigned Depth = 8, + parameter int unsigned EnqWidth = 2, + parameter int unsigned SelWidth = 2 +) ( + // Enqueue Port + output wire [EnqWidth-1:0][Depth-1:0] enq_mask_o, + // Select Port + input wire [Depth-1:0] sel_mask_i, + output wire [SelWidth-1:0][Depth-1:0] result_mask_o, + // Status + input wire [Depth-1:0] entry_vld_i +); + + + wire [EnqWidth-1:0][Depth-1:0] enq_allocated_mask; + wire [EnqWidth-1:0][Depth-1:0] masked_enq_rdy; + + wire [SelWidth-1:0][Depth-1:0] selected_mask; + wire [SelWidth-1:0][Depth-1:0] masked_sel_mask; + + generate + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_allocated_mask + if (i == 0) begin : gen_initial_one + assign enq_allocated_mask[i] = {Depth{1'b0}}; + end else begin : gen_others + assign enq_allocated_mask[i] = enq_allocated_mask[i-1] | enq_mask_o[i-1]; + end + assign masked_enq_rdy[i] = ~enq_allocated_mask[i] & ~entry_vld_i; + end + for (genvar i = 0; i < EnqWidth; i++) begin : gen_enq_mask + assign enq_mask_o[i] = masked_enq_rdy[i] & ~(masked_enq_rdy[i] - 1); + end + endgenerate + + + generate + for (genvar i = 0; i < SelWidth; i++) begin : gen_masked_sel_mask + if (i == 0) begin : gen_initial_one + assign selected_mask[i] = {Depth{1'b0}}; + end else begin : gen_others + assign selected_mask[i] = selected_mask[i-1] | result_mask_o[i-1]; + end + assign masked_sel_mask[i] = ~selected_mask[i] & sel_mask_i; + end + for (genvar i = 0; i < SelWidth; i++) begin : gen_sel_result + assign result_mask_o[i] = masked_sel_mask[i] & ~(masked_sel_mask[i] - 1); + end + endgenerate + + +endmodule diff --git a/rtl/util/commoncell/src/StdDFF/StdDFF.yaml b/rtl/util/commoncell/src/StdDFF/StdDFF.yaml new file mode 100644 index 0000000..5a88b55 --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/StdDFF.yaml @@ -0,0 +1,39 @@ +Name : StdDFF +Module: + - name: DFFUncertainChecker + description: DFF uncertain checker + language: SystemVerilog + rtl: + - dv/DFFUncertainChecker.v + + - name: DFF + description: Standard DFF + language: SystemVerilog + rtl: + - hw/DFF.v + dependency: + - DFFUncertainChecker + + - name: DFFE + description: Standard DFF with EN port + language: SystemVerilog + rtl: + - hw/DFFE.v + dependency: + - DFFUncertainChecker + + - name: DFFR + description: Standard DFF with RST port + language: SystemVerilog + rtl: + - hw/DFFR.v + dependency: + - DFFUncertainChecker + + - name: DFFRE + description: Standard DFF with EN and RST port + language: SystemVerilog + rtl: + - hw/DFFRE.v + dependency: + - DFFUncertainChecker \ No newline at end of file diff --git a/rtl/util/commoncell/src/StdDFF/dv/DFFUncertainChecker.v b/rtl/util/commoncell/src/StdDFF/dv/DFFUncertainChecker.v new file mode 100644 index 0000000..126275d --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/dv/DFFUncertainChecker.v @@ -0,0 +1,17 @@ +module DFFUncertainChecker #( + parameter int unsigned Width = 8 +) ( + input wire CLK, + input wire EN, + input reg [Width-1:0] Q +); + +`ifndef SYNTHESIS + CHECK_UNCERTAIN : + assert property (@(posedge CLK) EN |=> (|Q !== 1'bx)) + else begin + $fatal("\n Error : detected a X value which should never happen!\n"); + end +`endif + +endmodule diff --git a/rtl/util/commoncell/src/StdDFF/hw/DFF.v b/rtl/util/commoncell/src/StdDFF/hw/DFF.v new file mode 100644 index 0000000..9d9b59a --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/hw/DFF.v @@ -0,0 +1,25 @@ +module DFF #( + parameter int unsigned Width = 8 +) ( + input wire CLK, + input wire [Width-1:0] D, + output reg [Width-1:0] Q +); + + always @(posedge CLK) begin : dff + Q <= D; + end + +`ifndef SYNTHESIS + + DFFUncertainChecker #( + .Width(Width) + ) u_DFFUncertainChecker ( + .CLK(CLK), + .EN (1'b1), + .Q (Q) + ); + +`endif + +endmodule diff --git a/rtl/util/commoncell/src/StdDFF/hw/DFFE.v b/rtl/util/commoncell/src/StdDFF/hw/DFFE.v new file mode 100644 index 0000000..d00c9a3 --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/hw/DFFE.v @@ -0,0 +1,28 @@ +module DFFE #( + parameter int unsigned Width = 8 +) ( + input wire CLK, + input wire EN, + input wire [Width-1:0] D, + output reg [Width-1:0] Q +); + + always @(posedge CLK) begin : dff + if (EN) begin + Q <= D; + end + end + +`ifndef SYNTHESIS + + DFFUncertainChecker #( + .Width(Width) + ) u_DFFUncertainChecker ( + .CLK(CLK), + .EN (EN), + .Q (Q) + ); + +`endif + +endmodule diff --git a/rtl/util/commoncell/src/StdDFF/hw/DFFR.v b/rtl/util/commoncell/src/StdDFF/hw/DFFR.v new file mode 100644 index 0000000..a5c7b27 --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/hw/DFFR.v @@ -0,0 +1,31 @@ +module DFFR #( + parameter int unsigned Width = 8 +) ( + input wire CLK, + input wire RSTN, + input wire [Width-1:0] DRST, + input wire [Width-1:0] D, + output reg [Width-1:0] Q +); + + always @(posedge CLK or negedge RSTN) begin : dff + if (~RSTN) begin + Q <= DRST; + end else begin + Q <= D; + end + end + +`ifndef SYNTHESIS + + DFFUncertainChecker #( + .Width(Width) + ) u_DFFUncertainChecker ( + .CLK(CLK), + .EN (1'b1), + .Q (Q) + ); + +`endif + +endmodule diff --git a/rtl/util/commoncell/src/StdDFF/hw/DFFRE.v b/rtl/util/commoncell/src/StdDFF/hw/DFFRE.v new file mode 100644 index 0000000..863aca3 --- /dev/null +++ b/rtl/util/commoncell/src/StdDFF/hw/DFFRE.v @@ -0,0 +1,35 @@ +module DFFRE #( + parameter int unsigned Width = 8 +) ( + input wire CLK, + input wire RSTN, + input wire EN, + input wire [Width-1:0] DRST, + input wire [Width-1:0] D, + output reg [Width-1:0] Q +); + + always @(posedge CLK or negedge RSTN) begin : dff + if (~RSTN) begin + Q <= DRST; + end else begin + if (EN) begin + Q <= D; + end + end + end + +`ifndef SYNTHESIS + + DFFUncertainChecker #( + .Width(Width) + ) u_DFFUncertainChecker ( + .CLK(CLK), + .EN (EN), + .Q (Q) + ); + +`endif + + +endmodule diff --git a/rtl/util/commoncell/tools/pico/.gitignore b/rtl/util/commoncell/tools/pico/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/rtl/util/commoncell/tools/pico/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/rtl/util/commoncell/tools/pico/PackageParser.py b/rtl/util/commoncell/tools/pico/PackageParser.py new file mode 100755 index 0000000..6e98a64 --- /dev/null +++ b/rtl/util/commoncell/tools/pico/PackageParser.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3 + +from distutils.log import error, fatal +from pathlib import Path +from sys import argv +import yaml + + +class Module: + def __init__(self, root: Path, moduleMeta: dict) -> None: + self.rtlPathList = [] + self.simPathList = [] + self.dependModuleList = [] + if moduleMeta.get("dependency"): + for m in moduleMeta["dependency"]: + self.dependModuleList.append(m) + if moduleMeta.get("rtl"): + for m in moduleMeta["rtl"]: + self.rtlPathList.append(Path(root) / m) + if moduleMeta.get("sim"): + for m in moduleMeta["sim"]: + self.simPathList.append(Path(root) / m) + try: + self.name = moduleMeta["name"] + self.description = moduleMeta["description"] + self.language = moduleMeta["language"] + except yaml.YAMLError as e: + error(e) + + +class PackageParser: + def __init__(self, manifestPath: str): + with open(manifestPath) as f: + self.packageLocation = Path(manifestPath) + ROOT = self.packageLocation.parent + manifest = yaml.load(f, yaml.CLoader) + self.dependPackageDict = {} + self.ModuleDict = {} + if manifest.get("Dependency"): + for packagePath in manifest["Dependency"]: + filePath = Path(ROOT / packagePath) + package = PackageParser(filePath) + if not self.dependPackageDict.get(package.Name): + self.dependPackageDict[package.Name] = package + for (key, value) in package.ModuleDict.items(): + if not self.ModuleDict.get(key): + self.ModuleDict[key] = value + if manifest.get("Module"): + for module in manifest["Module"]: + self.addModule(ROOT, module) + try: + self.Name = manifest["Name"] + self.addModule(ROOT, self.packModule()) + except yaml.YAMLError as e: + error(e) + + def packModule(self): + packedModule = {} + packedModule['name'] = self.Name + packedModule['language'] = None + packedModule['description'] = "package {}".format(self.Name) + packedModule['dependency'] = [] + for module in self.ModuleDict: + packedModule['dependency'].append(module) + return packedModule + + + def addModule(self, root: Path, moduleMeta: dict) -> None: + self.ModuleDict[moduleMeta["name"]] = Module(root, moduleMeta) + if moduleMeta.get("sim"): + self.ModuleDict[moduleMeta["name"]+'_tb'] = Module(root, moduleMeta) + + def genModuleFileList(self, top: str, sim: bool): + if self.ModuleDict.get(top): + module = self.ModuleDict[top] + filelist = [] + for dependModule in module.dependModuleList: + for file in self.genModuleFileList(dependModule, False): + if file not in filelist: + filelist.append(file) + for file in module.rtlPathList: + absPath = str(file.absolute()) + if absPath not in filelist: + filelist.append((absPath,module.language)) + if sim: + for file in module.simPathList: + absPath = str(file.absolute()) + if absPath not in filelist: + filelist.append((absPath,module.language)) + return filelist + else: + fatal("Module {} doesn't exist".format(top)) + + def genPackageFileList(self, sim: bool): + filelist = [] + for module in self.ModuleDict: + for file in self.genModuleFileList(module, sim): + if file not in filelist : + filelist.append(file) + return filelist + + def genVcsFileList(self, sim: bool): + filelist = self.genPackageFileList(sim) + vcsfilelist = [] + for file in filelist: + vcsfilelist.append(file[0]) + return vcsfilelist + + def genEDAlizeFile(self,top: str, sim: bool): + edalizeFileList = [] + filelist = self.genModuleFileList(top,sim) + for (filePath,language) in filelist: + if language == "SystemVerilog" : + edalizeFileList.append( + {'name' : filePath, 'file_type' : 'systemVerilogSource'} + ) + elif language == "Verilog" : + edalizeFileList.append( + {'name' : filePath, 'file_type' : 'verilogSource'} + ) + elif language == "Vhdl": + edalizeFileList.append( + {'name' : filePath, 'file_type' : 'vhdlSource'} + ) + return edalizeFileList + + + +if __name__ == "__main__": + t = PackageParser(argv[1]) + for file in t.genVcsFileList(False): + print(file) diff --git a/rtl/util/commoncell/tools/pico/README.md b/rtl/util/commoncell/tools/pico/README.md new file mode 100644 index 0000000..1e1655e --- /dev/null +++ b/rtl/util/commoncell/tools/pico/README.md @@ -0,0 +1,2 @@ +# pico +Pico is a hardware dependency manangement tool which integrate Edalize to support different cad flow \ No newline at end of file diff --git a/rtl/util/commoncell/tools/pico/defaultToolOption.yaml b/rtl/util/commoncell/tools/pico/defaultToolOption.yaml new file mode 100644 index 0000000..ab0864e --- /dev/null +++ b/rtl/util/commoncell/tools/pico/defaultToolOption.yaml @@ -0,0 +1,8 @@ +vcs : + vcs_options : [-full64,-sverilog,+v2k,-debug_access+all,-kdb] + run_options : [] +spyglass: + methodology : GuideWare/latest/block/rtl_handoff + goals : [ 'lint/lint_rtl' ] + spyglass_parameters : [] + rule_parameters : [] \ No newline at end of file diff --git a/rtl/util/commoncell/tools/pico/pico b/rtl/util/commoncell/tools/pico/pico new file mode 100755 index 0000000..3214cf0 --- /dev/null +++ b/rtl/util/commoncell/tools/pico/pico @@ -0,0 +1,127 @@ +#!/usr/bin/python3 + +import argparse +from distutils.log import fatal +from genericpath import getmtime +import os +from pathlib import Path +from sys import argv +import yaml +from time import ctime, gmtime, strftime, time +from edalize import * +import edalize +from PackageParser import PackageParser + +SIM_TOOL = [ + 'vcs', 'verilator' +] + +TOOLS = [tool for tool in edalize.walk_tool_packages()] + +def getTime(): + return strftime("%m%d_%H_%M", gmtime()) + + +class PicoArgsParser: + def __init__(self, argv) -> None: + argParser = argparse.ArgumentParser(usage='Pico.py [manifest] -top [topmodule] -tool [vcs/verilator/veriblelint .etc] -configure -build -run',prefix_chars="-+") + argParser.add_argument('manifest', type=str, help="Project manifest") + argParser.add_argument( + '+args', nargs='?', action='append', help="Compile arguments") + argParser.add_argument( + '-tool', type=str, choices=TOOLS, required=True, help="EDA tool") + argParser.add_argument( + '-top', type=str, required=True, help="Top module") + argParser.add_argument('-trace', action='store_true', + default=False, help="Enable Waveform") + argParser.add_argument( + '+define', nargs='?', action='append', type=str, help="Verilog defination") + argParser.add_argument('-workdir', type=str, help="Work space") + argParser.add_argument('-build', action='store_true', + default=False, help="Build Project") + argParser.add_argument('-run', action='store_true', + default=False, help="Run Project") + argParser.add_argument('+runtime', action='append', help="runtime args") + argParser.add_argument('-cov','-coverage', dest='coverage', action='store_true',help='Enable coverage') + self.config = argParser.parse_args(argv) + + self.tool = self.config.tool + self.toplevel = self.config.top + self.build = self.config.build + self.run = self.config.run + self.tool_options = self.buildToolOptions() + self.parameters = self.buildDefination() + self.file = self.buildFlist() + self.edam = { + 'files': self.file, + 'name': self.toplevel, + 'parameters': self.parameters, + 'tool_options': self.tool_options, + 'toplevel': self.toplevel + } + self.workSpace = self.getWorkSpace() + + def getWorkSpace(self): + if self.config.workdir: + work_root = self.config.workdir + else: + work_root = self.tool + '_' + self.toplevel + '_' + getTime() + return work_root + + def buildFlist(self): + manifestParser = PackageParser(self.config.manifest) + simulationEnable = self.tool in SIM_TOOL + return manifestParser.genEDAlizeFile(self.toplevel, simulationEnable) + + def buildToolOptions(self): + config = self.config + tool_options = yaml.load( + open(Path(__file__).parent / "defaultToolOption.yaml"), yaml.CLoader) + if config.args: + if config.tool == 'vcs': + tool_options['vcs']['vcs_options'] += config.args + if config.runtime: + if config.tool == 'vcs': + tool_options['vcs']['run_options'] += config.runtime + if config.coverage: + if config.tool == 'vcs': + tool_options['vcs']['vcs_options'] += ['-cm line+cond+tgl+fsm+branch+assert'] + tool_options['vcs']['run_options'] += ['-cm line+cond+tgl+fsm+branch+assert'] + return tool_options + + def buildDefination(self): + parameters = {} + config = self.config + if config.trace: + parameters["DUMPON"] = { + 'datatype': 'bool', + 'default': 1, + 'paramtype': 'vlogdefine' + } + if config.define: + for define in config.define: + for item in define.split(','): + parameters[item] = { + 'datatype': 'bool', + 'default': 1, + 'paramtype': 'vlogdefine' + } + return parameters + +if __name__ == "__main__": + + config = PicoArgsParser(argv[1:]) + + work_root = config.workSpace + + backend = get_edatool(config.tool)(edam=config.edam, work_root=work_root) + + if not os.path.exists(work_root): + os.makedirs(work_root) + + if config.build: + backend.configure() + backend.build() + + if config.run: + backend.run() diff --git a/rtl/util/freelist.sv b/rtl/util/freelist.sv new file mode 100755 index 0000000..62a763f --- /dev/null +++ b/rtl/util/freelist.sv @@ -0,0 +1,148 @@ +`ifndef __FREELIST_SV__ +`define __FREELIST_SV__ + +module freelist +#( + parameter int unsigned ENTRY_COUNT = 4, + localparam int unsigned ENTRY_TAG_WIDTH = $clog2(ENTRY_COUNT), + localparam int unsigned IS_LOG2 = (2 ** ENTRY_TAG_WIDTH) == ENTRY_COUNT +) +( + // Enqueue + input logic enq_vld_i, + input logic[ENTRY_TAG_WIDTH-1:0] enq_tag_i, + // Dequeue + input logic deq_vld_i, + output logic[ENTRY_TAG_WIDTH-1:0] deq_tag_o, + output logic deq_rdy_o, + // Flush + input logic flush_i, + + input clk, + input rst +); + + logic [ENTRY_COUNT-1:0][ENTRY_TAG_WIDTH-1:0] ram_r,ram_n; + logic [ENTRY_COUNT-1:0] ram_clk_en; + + logic [ENTRY_TAG_WIDTH-1:0] head_r,head_n; + logic head_flag_r,head_flag_n; + logic head_clk_en; + + logic [ENTRY_TAG_WIDTH-1:0] tail_r,tail_n; + logic tail_flag_r,tail_flag_n; + logic tail_clk_en; + + logic enq_fire; + logic deq_fire; + + logic full,empty; + logic flag_equal,tag_equal; + + assign enq_fire = enq_vld_i; + assign deq_fire = deq_rdy_o & deq_vld_i; + + assign flag_equal = tail_flag_r == head_flag_r; + assign tag_equal = tail_r == head_r; + + assign full = ~flag_equal & tag_equal; + assign empty = flag_equal & tag_equal; + + // Output + assign deq_tag_o = ram_r[head_r]; + + always_comb begin : ram_update + ram_n = ram_r; + ram_n[tail_r] = enq_tag_i; + if(flush_i) begin + for(int i = 0 ; i < ENTRY_COUNT; i++) begin + ram_n[i] = i[ENTRY_TAG_WIDTH-1:0]; + end + end + end + + always_comb begin : ram_clk_en_gen + for(int i = 0 ; i < ENTRY_COUNT; i++) begin + ram_clk_en[i] = (enq_fire & (i == tail_r)) | flush_i; + end + end + + + always_comb begin : head_update + if(IS_LOG2) begin + {head_flag_n,head_n} = {head_flag_r,head_r} + 1'b1; + end else begin + if(head_r == ENTRY_COUNT-1) begin + head_n = {ENTRY_COUNT{1'b0}}; + head_flag_n = ~head_flag_r; + end else begin + head_n = head_r + 1'b1; + head_flag_n = head_flag_r; + end + end + if(flush_i) begin + {head_flag_n,head_n} = {head_flag_r,head_r}; + end + end + + assign tail_clk_en = enq_fire | flush_i; + assign deq_rdy_o = ~empty; + + always_comb begin : tail_update + if(IS_LOG2) begin + {tail_flag_n,tail_n} = {tail_flag_r,tail_r} + 1'b1; + end else begin + if(tail_r == ENTRY_COUNT-1) begin + tail_n = {ENTRY_TAG_WIDTH{1'b0}}; + tail_flag_n = ~tail_flag_r; + end else begin + tail_n = tail_r + 1'b1; + tail_flag_n = tail_flag_r; + end + end + if(flush_i) begin + {tail_flag_n,tail_n} = {~head_flag_r,head_r}; + end + end + + assign head_clk_en = deq_fire | flush_i; + + always_ff@(posedge clk) begin : head_dff + if(rst) begin + {head_flag_r,head_r} <= {(ENTRY_TAG_WIDTH+1){1'b0}}; + end else begin + if(head_clk_en) begin + {head_flag_r,head_r} <= {head_flag_n,head_n}; + end + end + end + + always_ff@(posedge clk) begin : tail_dff + if(rst) begin + {tail_flag_r,tail_r} <= {1'b1,{(ENTRY_TAG_WIDTH){1'b0}}}; + end else begin + if(tail_clk_en) begin + {tail_flag_r,tail_r} <= {tail_flag_n,tail_n}; + end + end + end + + always_ff@(posedge clk) begin : ram_dff + if(rst) begin + for(int i = 0 ; i < ENTRY_COUNT; i++) begin + ram_r[i] <= i[ENTRY_TAG_WIDTH-1:0]; + end + end else begin + for(int i = 0 ; i < ENTRY_COUNT; i++) begin + if(ram_clk_en[i]) begin + ram_r[i] <= ram_n[i]; + end + end + end + end + + + +endmodule : freelist + +`endif \ No newline at end of file diff --git a/rtl/util/inorder_router.sv b/rtl/util/inorder_router.sv new file mode 100644 index 0000000..ba38dc2 --- /dev/null +++ b/rtl/util/inorder_router.sv @@ -0,0 +1,67 @@ +`ifndef __INORDER_ROUTER_SV__ +`define __INORDER_ROUTER_SV__ +module inorder_router +#( + parameter int unsigned SRC_COUNT = 4, + parameter int unsigned DES_COUNT = 4 +) ( + input logic[SRC_COUNT-1:0] src_vld_i, + input logic[SRC_COUNT-1:0][DES_COUNT-1:0] src_des_en_i, + output logic[SRC_COUNT-1:0] success_o, + output logic[DES_COUNT-1:0][SRC_COUNT-1:0] src_des_sel_o +); + logic[SRC_COUNT-1:0] success; + logic[SRC_COUNT-1:0][DES_COUNT-1:0] des_sel_mask; + logic[SRC_COUNT-1:0][DES_COUNT-1:0] des_disable_mask; + logic[SRC_COUNT-1:0][DES_COUNT-1:0] des_rdy_mask; + + generate + for(genvar src = 0 ; src < SRC_COUNT; src++) begin : gen_des_disable_mask + if(src == 0) begin + assign des_disable_mask[src] = {DES_COUNT{1'b0}}; + end else begin + assign des_disable_mask[src] = des_disable_mask[src-1] | des_sel_mask[src-1]; + end + end + endgenerate + + + generate + for(genvar src = 0 ; src < SRC_COUNT ; src++) begin : gen_des_rdy_mask + if(src == 0) begin + assign des_rdy_mask[src] = {DES_COUNT{src_vld_i[src]}} & src_des_en_i[src]; + end else begin + assign des_rdy_mask[src] = {DES_COUNT{src_vld_i[src]}} & src_des_en_i[src] & ~des_disable_mask[src] & {DES_COUNT{success[src-1]}}; + end + end + endgenerate + + generate + for(genvar src = 0 ; src < SRC_COUNT ; src++) begin : gen_success_vec + assign success[src] = |des_sel_mask[src]; + end + endgenerate + + always_comb begin : output_logic + success_o = success; + for(int src = 0 ; src < SRC_COUNT; src++) begin + for(int des = 0 ; des < DES_COUNT; des++) begin + src_des_sel_o[des][src] = des_sel_mask[src][des]; + end + end + end + generate + for(genvar src = 0 ; src < SRC_COUNT ; src++) begin + one_hot_priority_encoder + #( + .SEL_WIDTH(DES_COUNT) + ) u_routing_encoder ( + .sel_i(des_rdy_mask[src]), + .sel_o(des_sel_mask[src]) + ); + end + endgenerate + +endmodule + +`endif \ No newline at end of file diff --git a/rtl/util/left_circular_rotate.sv b/rtl/util/left_circular_rotate.sv new file mode 100644 index 0000000..b6630a1 --- /dev/null +++ b/rtl/util/left_circular_rotate.sv @@ -0,0 +1,15 @@ +module left_circular_rotate #( + parameter N_INPUT = 2, + localparam int unsigned N_INPUT_WIDTH = N_INPUT > 1 ? $clog2(N_INPUT) : 1 +) ( + input logic [N_INPUT-1:0] ori_vector_i, + input logic [N_INPUT_WIDTH-1:0] req_left_rotate_num_i, + + output logic [N_INPUT-1:0] roteted_vector_o +); + + logic [N_INPUT*2-1:0] ori_vector_mid; + assign ori_vector_mid = {ori_vector_i, ori_vector_i} << req_left_rotate_num_i; + assign roteted_vector_o = ori_vector_mid[N_INPUT*2-1-:N_INPUT]; + +endmodule diff --git a/rtl/util/mp_fifo.sv b/rtl/util/mp_fifo.sv new file mode 100755 index 0000000..cf373b1 --- /dev/null +++ b/rtl/util/mp_fifo.sv @@ -0,0 +1,91 @@ +`ifndef __MP_FIFO_SV__ +`define __MP_FIFO_SV__ + +module mp_fifo +#( + parameter type payload_t = logic[3:0], + parameter int unsigned ENQUEUE_WIDTH = 4, + parameter int unsigned DEQUEUE_WIDTH = 4, + parameter int unsigned DEPTH = 16, + parameter int unsigned MUST_TAKEN_ALL = 1 +) +( + // Enqueue + input logic[ENQUEUE_WIDTH-1:0] enqueue_vld_i, + input payload_t[ENQUEUE_WIDTH-1:0] enqueue_payload_i, + output logic[ENQUEUE_WIDTH-1:0] enqueue_rdy_o, + // Dequeue + output logic[DEQUEUE_WIDTH-1:0] dequeue_vld_o, + output payload_t[DEQUEUE_WIDTH-1:0] dequeue_payload_o, + input logic[DEQUEUE_WIDTH-1:0] dequeue_rdy_i, + + input logic flush_i, + + input clk, + input rst +); + + localparam int unsigned ENTRY_PTR_WIDTH = $clog2(DEPTH); + localparam int unsigned ENTRY_CNT_WIDTH = $clog2(DEPTH+1); + + logic[ENQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] enq_ptr; + logic[DEQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] deq_ptr; + logic[ENTRY_CNT_WIDTH-1:0] avail_cnt; + + payload_t[DEPTH-1:0] payload_dff; + + + logic[ENQUEUE_WIDTH-1:0] enq_fire; + logic[DEQUEUE_WIDTH-1:0] deq_fire; + + assign enq_fire = enqueue_vld_i & enqueue_rdy_o; + assign deq_fire = dequeue_vld_o & dequeue_rdy_i; + + generate + for(genvar i = 0 ; i < DEQUEUE_WIDTH; i++) begin + assign dequeue_payload_o[i] = payload_dff[deq_ptr[i]]; + assign dequeue_vld_o[i] = (DEPTH-avail_cnt) > i; + end + endgenerate + + generate + if(MUST_TAKEN_ALL) begin + assign enqueue_rdy_o = {ENQUEUE_WIDTH{avail_cnt >= ENQUEUE_WIDTH}}; + end else begin + for(genvar i = 0; i < ENQUEUE_WIDTH; i++) begin + assign enqueue_rdy_o[i] = avail_cnt > i; + end + end + endgenerate + + + always_ff@(posedge clk) begin : payload_dff_update + for(int i = 0; i < ENQUEUE_WIDTH; i++) begin + if(enq_fire[i]) begin + payload_dff[enq_ptr[i]] <= enqueue_payload_i[i]; + end + end + end + + usage_manager #( + .ENTRY_COUNT(DEPTH), + .ENQ_WIDTH(ENQUEUE_WIDTH), + .DEQ_WIDTH(DEQUEUE_WIDTH), + .FLAG_EN(0), + .INIT_IS_FULL(0), + .COMB_DEQ_EN(0), + .COMB_ENQ_EN(1) + ) u_usage_manager ( + .enq_fire_i(enq_fire), + .deq_fire_i(deq_fire), + .head_o(deq_ptr), + .tail_o(enq_ptr), + .avail_cnt_o(avail_cnt), + .flush_i(flush_i), + .clk(clk), + .rst(rst) + ); + +endmodule : mp_fifo + +`endif \ No newline at end of file diff --git a/rtl/util/mp_fifo_ptr_output.sv b/rtl/util/mp_fifo_ptr_output.sv new file mode 100755 index 0000000..9ec1c9c --- /dev/null +++ b/rtl/util/mp_fifo_ptr_output.sv @@ -0,0 +1,94 @@ +module mp_fifo_ptr_output +#( + parameter type payload_t = logic[3:0], + parameter int unsigned ENQUEUE_WIDTH = 4, + parameter int unsigned DEQUEUE_WIDTH = 4, + parameter int unsigned DEPTH = 16, + parameter int unsigned MUST_TAKEN_ALL = 1, + + localparam int unsigned ENTRY_PTR_WIDTH = $clog2(DEPTH), + localparam int unsigned ENTRY_CNT_WIDTH = $clog2(DEPTH+1) +) +( + // Enqueue + input logic[ENQUEUE_WIDTH-1:0] enqueue_vld_i, + input payload_t[ENQUEUE_WIDTH-1:0] enqueue_payload_i, + output logic[ENQUEUE_WIDTH-1:0] enqueue_rdy_o, + // Dequeue + output logic[DEQUEUE_WIDTH-1:0] dequeue_vld_o, + output payload_t[DEQUEUE_WIDTH-1:0] dequeue_payload_o, + input logic[DEQUEUE_WIDTH-1:0] dequeue_rdy_i, + + // ptr output + output logic[ENQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] enq_ptr_o, + output logic[DEQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] deq_ptr_o, + + input logic flush_i, + + input clk, + input rst +); + + logic[ENQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] enq_ptr; + logic[DEQUEUE_WIDTH-1:0][ENTRY_PTR_WIDTH-1:0] deq_ptr; + logic[ENTRY_CNT_WIDTH-1:0] avail_cnt; + + payload_t[DEPTH-1:0] payload_dff; + + + logic[ENQUEUE_WIDTH-1:0] enq_fire; + logic[DEQUEUE_WIDTH-1:0] deq_fire; + + assign enq_ptr_o = enq_ptr; + assign deq_ptr_o = deq_ptr; + + + assign enq_fire = enqueue_vld_i & enqueue_rdy_o; + assign deq_fire = dequeue_vld_o & dequeue_rdy_i; + + generate + for(genvar i = 0 ; i < DEQUEUE_WIDTH; i++) begin + assign dequeue_payload_o[i] = payload_dff[deq_ptr[i]]; + assign dequeue_vld_o[i] = (DEPTH-avail_cnt) > i; + end + endgenerate + + generate + if(MUST_TAKEN_ALL) begin + assign enqueue_rdy_o = {ENQUEUE_WIDTH{avail_cnt >= ENQUEUE_WIDTH}}; + end else begin + for(genvar i = 0; i < ENQUEUE_WIDTH; i++) begin + assign enqueue_rdy_o[i] = avail_cnt > i; + end + end + endgenerate + + + always_ff@(posedge clk) begin : payload_dff_update + for(int i = 0; i < ENQUEUE_WIDTH; i++) begin + if(enq_fire[i]) begin + payload_dff[enq_ptr[i]] <= enqueue_payload_i[i]; + end + end + end + + usage_manager #( + .ENTRY_COUNT(DEPTH), + .ENQ_WIDTH(ENQUEUE_WIDTH), + .DEQ_WIDTH(DEQUEUE_WIDTH), + .FLAG_EN(0), + .INIT_IS_FULL(0), + .COMB_DEQ_EN(0), + .COMB_ENQ_EN(1) + ) u_usage_manager ( + .enq_fire_i(enq_fire), + .deq_fire_i(deq_fire), + .head_o(deq_ptr), + .tail_o(enq_ptr), + .avail_cnt_o(avail_cnt), + .flush_i(flush_i), + .clk(clk), + .rst(rst) + ); + +endmodule : mp_fifo_ptr_output diff --git a/rtl/util/mp_freelist.sv b/rtl/util/mp_freelist.sv new file mode 100755 index 0000000..2b60f87 --- /dev/null +++ b/rtl/util/mp_freelist.sv @@ -0,0 +1,134 @@ +`ifndef __MP_FREELIST_SV__ +`define __MP_FREELIST_SV__ + + +module mp_freelist #( + parameter int unsigned ENTRY_COUNT = 12, + parameter int unsigned ALLOC_WIDTH = 4, + parameter int unsigned DEALLOC_WIDTH = 4, + parameter bit MUST_TAKEN_ALL = 1, + localparam int unsigned TAG_WIDTH = $clog2(ENTRY_COUNT) +) ( + // Enqueue + input logic [ALLOC_WIDTH-1:0] alloc_vld_i, + output logic [ALLOC_WIDTH-1:0][TAG_WIDTH-1:0] alloc_tag_o, + output logic [ALLOC_WIDTH-1:0] alloc_rdy_o, + // Dequeue + input logic [DEALLOC_WIDTH-1:0] dealloc_vld_i, + input logic [DEALLOC_WIDTH-1:0][TAG_WIDTH-1:0] dealloc_tag_i, + + input logic flush_i, + + input clk, + input rst +); + localparam int unsigned AVAIL_CNT_WIDTH = $clog2(ENTRY_COUNT + 1); + + // Clk gate + logic [ENTRY_COUNT-1:0] ram_clk_en; + + + // Status + logic [DEALLOC_WIDTH-1:0][TAG_WIDTH-1:0] head; + logic [ALLOC_WIDTH-1:0][TAG_WIDTH-1:0] tail; + logic [AVAIL_CNT_WIDTH-1:0] usage; + + logic [ENTRY_COUNT-1:0][TAG_WIDTH-1:0] ram_d, ram_q; + + + // Allocate + logic [ALLOC_WIDTH-1:0] alloc_fire; + logic [ALLOC_WIDTH-1:0][TAG_WIDTH-1:0] alloc_entry_ptr; + + + // Deallocate + logic [DEALLOC_WIDTH-1:0] dealloc_fire; + logic [DEALLOC_WIDTH-1:0][TAG_WIDTH-1:0] dealloc_entry_ptr; + + + always_comb begin : gen_alloc_rdy + if (MUST_TAKEN_ALL) begin + for (int i = 0; i < ALLOC_WIDTH; i++) begin + alloc_rdy_o[i] = (ENTRY_COUNT - usage) >= ALLOC_WIDTH; + end + end else begin + for (int i = 0; i < ALLOC_WIDTH; i++) begin + alloc_rdy_o[i] = (ENTRY_COUNT - usage) > i; + end + end + end + + generate + for (genvar i = 0; i < ALLOC_WIDTH; i++) begin : gen_alloc_tag + assign alloc_tag_o[i] = ram_q[alloc_entry_ptr[i]]; + end + endgenerate + + assign alloc_fire = alloc_vld_i & alloc_rdy_o; + + assign alloc_entry_ptr = head; + + + + assign dealloc_fire = dealloc_vld_i; + + assign dealloc_entry_ptr = tail; + + always_comb begin : gen_ram_clk_en + ram_clk_en = {ENTRY_COUNT{flush_i}}; + for (int i = 0; i < DEALLOC_WIDTH; i++) begin + if(dealloc_fire[i]) begin + ram_clk_en[dealloc_entry_ptr[i]] = 1'b1; + end + end + end + + always_comb begin : ram_update + ram_d = ram_q; + for (int i = 0; i < DEALLOC_WIDTH; i++) begin + ram_d[dealloc_entry_ptr[i]] = dealloc_tag_i[i]; + end + if (flush_i) begin + for (int i = 0; i < ENTRY_COUNT; i++) begin + ram_d[i] = i[TAG_WIDTH-1:0]; + end + end + end + + always_ff @(posedge clk) begin : ram_dff + if (rst) begin + for (int i = 0; i < ENTRY_COUNT; i++) begin + ram_q[i] <= i[TAG_WIDTH-1:0]; + end + end else begin + for (int i = 0; i < ENTRY_COUNT; i++) begin + if (ram_clk_en[i]) begin + ram_q[i] <= ram_d[i]; + end + end + end + end + + + usage_manager #( + .ENTRY_COUNT(ENTRY_COUNT), + .ENQ_WIDTH(DEALLOC_WIDTH), + .DEQ_WIDTH(ALLOC_WIDTH), + .FLAG_EN(0), + .INIT_IS_FULL(1), + .COMB_DEQ_EN(1), + .COMB_ENQ_EN(1) + ) u_usage_manager ( + .enq_fire_i(dealloc_fire), + .deq_fire_i(alloc_fire), + .head_o(head), + .tail_o(tail), + .avail_cnt_o(usage), + .flush_i(flush_i), + .clk(clk), + .rst(rst) + ); + +endmodule : mp_freelist + +`endif diff --git a/rtl/util/oh2idx.sv b/rtl/util/oh2idx.sv new file mode 100644 index 0000000..a7b7884 --- /dev/null +++ b/rtl/util/oh2idx.sv @@ -0,0 +1,29 @@ +module oh2idx +#( + parameter int unsigned N_INPUT = 2, + localparam int unsigned N_INPUT_WIDTH = N_INPUT > 1 ? $clog2(N_INPUT) : 1 +) +( + input [N_INPUT-1:0] oh_i, + output [N_INPUT_WIDTH-1:0] idx_o +); + +genvar i, j; + +logic [N_INPUT_WIDTH-1:0][N_INPUT-1:0] mask; + +generate + for(i = 0; i < N_INPUT_WIDTH; i++) begin: gen_mask_i + for(j = 0; j < N_INPUT; j++) begin: gen_mask_j + assign mask[i][j] = (j/(2**i)) % 2; + end + end +endgenerate + +generate + for(i = 0; i < N_INPUT_WIDTH; i++) begin: gen_idx_o + assign idx_o[i] = |(oh_i & mask[i]); + end +endgenerate + +endmodule diff --git a/rtl/util/one_counter.sv b/rtl/util/one_counter.sv new file mode 100755 index 0000000..66f596d --- /dev/null +++ b/rtl/util/one_counter.sv @@ -0,0 +1,50 @@ +`ifndef __ONE_COUNTER_SV__ +`define __ONE_COUNTER_SV__ + +module one_counter +#( + parameter int unsigned DATA_WIDTH = 8, + localparam int unsigned CNT_WIDTH = $clog2(DATA_WIDTH) + 1 +) +( + input logic[DATA_WIDTH-1:0] data_i, + output logic[CNT_WIDTH-1:0] cnt_o +); + localparam int unsigned PADDED_DATA_WIDTH = 1 << $clog2(DATA_WIDTH); + + logic [PADDED_DATA_WIDTH-1:0] padded_data; + + always_comb begin + padded_data = {PADDED_DATA_WIDTH{1'b0}}; + padded_data[DATA_WIDTH-1:0] = data_i; + end + + if(DATA_WIDTH == 1) begin + assign cnt_o = data_i; + end else begin + + logic [CNT_WIDTH-2:0] res_left,res_right; + + assign cnt_o = res_left + res_right; + + one_counter #( + .DATA_WIDTH(PADDED_DATA_WIDTH/2) + ) one_counter_left_u ( + .data_i(padded_data[PADDED_DATA_WIDTH-1:PADDED_DATA_WIDTH/2]), + .cnt_o(res_left) + ); + one_counter #( + .DATA_WIDTH(PADDED_DATA_WIDTH/2) + ) one_counter_right_u ( + .data_i(padded_data[PADDED_DATA_WIDTH/2-1:0]), + .cnt_o(res_right) + ); + end + + + + + +endmodule : one_counter + +`endif \ No newline at end of file diff --git a/rtl/util/one_hot_priority_encoder.sv b/rtl/util/one_hot_priority_encoder.sv new file mode 100755 index 0000000..9d80601 --- /dev/null +++ b/rtl/util/one_hot_priority_encoder.sv @@ -0,0 +1,28 @@ +`ifndef __ONE_HOT_PRIORITY_ENCODER_SV__ +`define __ONE_HOT_PRIORITY_ENCODER_SV__ + + +module one_hot_priority_encoder +#( + parameter int unsigned SEL_WIDTH = 8 +) +( + input logic[SEL_WIDTH-1:0] sel_i, + output logic[SEL_WIDTH-1:0] sel_o +); + + localparam int unsigned SEL_ID_WIDHT = $clog2(SEL_WIDTH); + + + logic [SEL_WIDTH-1:0] sel_mask; + + assign sel_mask = ((~sel_i + 1'b1) & sel_i); + + assign sel_o = sel_mask; + + + + +endmodule : one_hot_priority_encoder + +`endif \ No newline at end of file diff --git a/rtl/util/one_hot_rr_arb.sv b/rtl/util/one_hot_rr_arb.sv new file mode 100755 index 0000000..4412b2f --- /dev/null +++ b/rtl/util/one_hot_rr_arb.sv @@ -0,0 +1,128 @@ +module one_hot_rr_arb #( + parameter N_INPUT = 2, + localparam int unsigned N_INPUT_WIDTH = N_INPUT > 1 ? $clog2(N_INPUT) : 1, + localparam int unsigned IS_LOG2 = (2 ** N_INPUT_WIDTH) == N_INPUT, + + parameter TIMEOUT_UPDATE_EN = 0, + parameter TIMEOUT_UPDATE_CYCLE = 10 +) ( + input logic [N_INPUT-1:0] req_i, + input logic update_i, + output logic [N_INPUT-1:0] grt_o, + output logic [N_INPUT_WIDTH-1:0] grt_idx_o, + input logic rstn, clk +); + + logic [$clog2(TIMEOUT_UPDATE_CYCLE)-1:0] timeout_counter_q, timeout_counter_d; + logic timeout_counter_add, timeout_counter_clr; + logic timeout_counter_en; + logic timeout_en; + + generate + if(N_INPUT == 1) begin: gen_one_hot_rr_arb_one_input + assign grt_o = req_i; + assign grt_idx_o = 0; + end else begin: gen_one_hot_rr_arb_common_input + + logic req_vld; + logic [N_INPUT*2-1:0] reversed_dereordered_selected_req_pre_shift, reversed_dereordered_selected_req_shift; + logic [N_INPUT-1:0] reodered_req, reordered_selected_req; + logic [N_INPUT-1:0] dereordered_selected_req; + logic [N_INPUT-1:0] reversed_reordered_selected_req, reversed_dereordered_selected_req; + logic [N_INPUT_WIDTH-1:0] round_ptr_q, round_ptr_d; + logic [N_INPUT_WIDTH-1:0] round_ptr_q_comp; + logic [N_INPUT_WIDTH-1:0] oh_to_idx; + logic [N_INPUT_WIDTH-1:0] selected_req_idx; + + assign req_vld = update_i | timeout_en; + + always_ff @(posedge clk or negedge rstn) begin + if (~rstn) begin + round_ptr_q <= '0; + end else begin + if (req_vld) begin + round_ptr_q <= round_ptr_d; + end + end + end + + assign round_ptr_q_comp = N_INPUT - round_ptr_q; + + //7 6 5 4 3 2 1 0 // req_i + //2 1 0 7 6 5 4 3 // reodered_req + //7 6 5 4 3 2 1 0 // dereordered_selected_req + + left_circular_rotate + #( + .N_INPUT(N_INPUT ) + ) + left_circular_rotate_reodered_req_u ( + .ori_vector_i (req_i ), + .req_left_rotate_num_i (round_ptr_q ), + .roteted_vector_o ( reodered_req) + ); + + one_hot_priority_encoder + #( + .SEL_WIDTH (N_INPUT) + ) + biased_one_hot_priority_encoder_u + ( + .sel_i (reodered_req ), + .sel_o (reordered_selected_req ) + ); + + left_circular_rotate + #( + .N_INPUT(N_INPUT ) + ) + left_circular_rotate_dereordered_selected_req_u ( + .ori_vector_i (reordered_selected_req ), + .req_left_rotate_num_i (round_ptr_q_comp ), + .roteted_vector_o ( dereordered_selected_req) + ); + + oh2idx + #( + .N_INPUT(N_INPUT ) + ) + oh2idx_u ( + .oh_i (dereordered_selected_req ), + .idx_o (oh_to_idx) + ); + + assign selected_req_idx = oh_to_idx[N_INPUT_WIDTH-1:0]; + + assign round_ptr_d = (selected_req_idx == '0) ? N_INPUT-1 : + (selected_req_idx == (N_INPUT-1)) ? '0 : + (N_INPUT-1) - selected_req_idx; + + assign grt_o = dereordered_selected_req; + assign grt_idx_o = selected_req_idx; + + + + + // timeout update + if(TIMEOUT_UPDATE_EN) begin + assign timeout_counter_add = (|req_i) & ~req_vld; + assign timeout_counter_clr = req_vld; + assign timeout_counter_d = timeout_counter_clr ? '0 : timeout_counter_q + 1; + assign timeout_counter_en = timeout_counter_add | (timeout_counter_clr & (timeout_counter_q != '0)); + always @(posedge clk or negedge rstn) begin + if (~rstn) begin + timeout_counter_q <= '0; + end else begin + if (timeout_counter_en) begin + timeout_counter_q <= timeout_counter_d; + end + end + end + + assign timeout_en = (timeout_counter_q == TIMEOUT_UPDATE_CYCLE); + end else begin + assign timeout_en = '0; + end + end + endgenerate +endmodule diff --git a/rtl/util/onehot_mux.sv b/rtl/util/onehot_mux.sv new file mode 100755 index 0000000..be51732 --- /dev/null +++ b/rtl/util/onehot_mux.sv @@ -0,0 +1,37 @@ +`ifndef __ONEHOT_MUX_SV__ +`define __ONEHOT_MUX_SV__ + +module onehot_mux +#( + parameter int unsigned SOURCE_COUNT = 2, + parameter int unsigned DATA_WIDTH = 1 +) +( + input logic[SOURCE_COUNT-1:0] sel_i, + input logic[SOURCE_COUNT-1:0][DATA_WIDTH-1:0] data_i, + output logic[DATA_WIDTH-1:0] data_o +); + + logic[DATA_WIDTH-1:0][SOURCE_COUNT-1:0] trans_data; + logic[DATA_WIDTH-1:0][SOURCE_COUNT-1:0] select_mat; + + generate + for(genvar i = 0 ; i < DATA_WIDTH; i++) begin + for(genvar j = 0 ; j < SOURCE_COUNT; j++) begin + assign trans_data[i][j] = data_i[j][i]; + end + end + endgenerate + + generate + for(genvar i = 0; i < DATA_WIDTH; i++) begin + assign select_mat[i] = trans_data[i] & sel_i; + assign data_o[i] = |select_mat[i]; + end + endgenerate + + + +endmodule + +`endif \ No newline at end of file diff --git a/rtl/util/plru.sv b/rtl/util/plru.sv new file mode 100644 index 0000000..3415e8c --- /dev/null +++ b/rtl/util/plru.sv @@ -0,0 +1,121 @@ +`ifndef __PLRU_SV__ +`define __PLRU_SV__ + +module plru +#( + parameter int unsigned N_WAYS = 2, + parameter int unsigned N_SETS = 2 +) +( + input logic touch_valid_i, + input logic [$clog2(N_SETS)-1:0] touch_addr_i, + input logic [$clog2(N_WAYS)-1:0] touch_way_i, + + output logic [$clog2(N_WAYS)-1:0] alloc_way_o, + + input logic clk, + input logic rst +); + +logic [N_SETS-1:0][N_WAYS-2:0] plru_regs; + +generate + if(N_WAYS == 2) begin + assign alloc_way_o = plru_regs[touch_addr_i][0]; + end + else if (N_WAYS == 4) begin + assign alloc_way_o = plru_regs[touch_addr_i][0] ? + {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][2]} : + {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][1]}; + end + else if (N_WAYS == 8) begin + always_comb begin + if (plru_regs[touch_addr_i][0]) begin + if (plru_regs[touch_addr_i][2]) begin + alloc_way_o = {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][2], plru_regs[touch_addr_i][6]}; + end + else begin + alloc_way_o = {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][2], plru_regs[touch_addr_i][5]}; + end + end + else begin + if (plru_regs[touch_addr_i][1]) begin + alloc_way_o = {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][1], plru_regs[touch_addr_i][4]}; + end + else begin + alloc_way_o = {plru_regs[touch_addr_i][0], plru_regs[touch_addr_i][1], plru_regs[touch_addr_i][3]}; + end + end + end + end +endgenerate + +generate + if(N_WAYS == 2) begin + always_ff @(posedge clk) begin + if (rst) begin + plru_regs <= 0; + end + else if (touch_valid_i) begin + if (touch_way_i == alloc_way_o) begin + plru_regs[touch_addr_i] <= ~plru_regs[touch_addr_i]; + end + end + end + end + else if (N_WAYS == 4) begin + always_ff @(posedge clk) begin + if (rst) begin + plru_regs <= 0; + end + else if (touch_valid_i & (alloc_way_o == touch_way_i)) begin + if (plru_regs[touch_addr_i][0]) begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][2] <= ~plru_regs[touch_addr_i][2]; + end + else begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][1] <= ~plru_regs[touch_addr_i][1]; + end + end + end + end + else if (N_WAYS == 8) begin + always_ff @(posedge clk) begin + if (rst) begin + plru_regs <= 0; + end + else if (touch_valid_i & (alloc_way_o == touch_way_i)) begin + if (plru_regs[touch_addr_i][0]) begin + if (plru_regs[touch_addr_i][2]) begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][2] <= ~plru_regs[touch_addr_i][2]; + plru_regs[touch_addr_i][6] <= ~plru_regs[touch_addr_i][6]; + end + else begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][2] <= ~plru_regs[touch_addr_i][2]; + plru_regs[touch_addr_i][5] <= ~plru_regs[touch_addr_i][5]; + end + end + else begin + if (plru_regs[touch_addr_i][1]) begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][2] <= ~plru_regs[touch_addr_i][2]; + plru_regs[touch_addr_i][4] <= ~plru_regs[touch_addr_i][4]; + end + else begin + plru_regs[touch_addr_i][0] <= ~plru_regs[touch_addr_i][0]; + plru_regs[touch_addr_i][2] <= ~plru_regs[touch_addr_i][2]; + plru_regs[touch_addr_i][3] <= ~plru_regs[touch_addr_i][3]; + end + end + end + end + end +endgenerate + + +endmodule + +`endif \ No newline at end of file diff --git a/rtl/util/priority_encoder.sv b/rtl/util/priority_encoder.sv new file mode 100755 index 0000000..ef89089 --- /dev/null +++ b/rtl/util/priority_encoder.sv @@ -0,0 +1,43 @@ +`ifndef __PRIORITY_ENCODER_SV__ +`define __PRIORITY_ENCODER_SV__ +module priority_encoder +#( + parameter int unsigned SEL_WIDTH = 8, + localparam int unsigned SEL_ID_WIDHT = SEL_WIDTH > 1 ? $clog2(SEL_WIDTH) : 1 +) +( + input logic[SEL_WIDTH-1:0] sel_i, + output logic id_vld_o, + output logic[SEL_ID_WIDHT-1:0] id_o +); + + if(SEL_WIDTH == 1) begin + + assign id_vld_o = sel_i; + assign id_o = 1'b0; + + end + else begin + + logic [SEL_WIDTH-1:0] sel_mask; + logic [SEL_ID_WIDHT+1-1:0] sel_result; + + assign sel_mask = ((~sel_i + 1'b1) & sel_i) - 1'b1; + + one_counter #( + .DATA_WIDTH(SEL_WIDTH) + ) one_counter_u ( + .data_i(sel_mask), + .cnt_o(sel_result) + ); + + assign id_o = sel_result[SEL_ID_WIDHT-1:0]; + assign id_vld_o = |sel_i; + + end + + + +endmodule : priority_encoder + +`endif \ No newline at end of file diff --git a/rtl/util/pseudo_dual_ram.sv b/rtl/util/pseudo_dual_ram.sv new file mode 100755 index 0000000..cd27b2c --- /dev/null +++ b/rtl/util/pseudo_dual_ram.sv @@ -0,0 +1,82 @@ +`ifndef __PSEUDO_DUAL_RAM_SV__ +`define __PSEUDO_DUAL_RAM_SV__ + +module pseudo_dual_ram +#( + parameter int unsigned WIDTH = 8, + parameter int unsigned DEPTH = 8 +) +( + input logic [$clog2(DEPTH)-1:0] ra, + input logic re, + output logic [WIDTH-1:0] rd, + + input logic [$clog2(DEPTH)-1:0] wa, + input logic we, + input logic [WIDTH-1:0] wd, + + input logic rst, + input logic clk +); + +logic [DEPTH-1:0][WIDTH-1:0] ram; + +logic [$clog2(DEPTH)-1:0] ra_ff; +logic re_ff; +logic [$clog2(DEPTH)-1:0] wa_ff; +logic we_ff; +logic [WIDTH-1:0] wd_ff; + +//sync +always_ff@(posedge clk) begin + if (rst) begin + ra_ff <= 0; + re_ff <= 0; + end + else begin + ra_ff <= ra; + re_ff <= re; + end +end + +always_ff@(posedge clk) begin + if (rst) begin + wa_ff <= 0; + we_ff <= 0; + wd_ff <= 0; + end + else begin + wa_ff <= wa; + we_ff <= we; + wd_ff <= wd; + end +end + +// read +always_comb begin + rd = 0; + if (re_ff) begin + // write bypass read + if (we_ff && wa_ff == ra_ff) begin + rd = wd_ff; + end + else begin + rd = ram[ra_ff]; + end + end +end + +// write +always_ff@(posedge clk) begin + if (rst) begin + for (int i=0; i 1 ? $clog2(SEL_WIDTH) : 1 +) +( + input logic[SEL_WIDTH-1:0] sel_i, + input logic first_id_needed_vld_i, + input logic second_id_needed_vld_i, + output logic first_id_vld_o, + output logic second_id_vld_o, + output logic[SEL_ID_WIDHT-1:0] first_id_o, + output logic[SEL_ID_WIDHT-1:0] second_id_o +); +genvar i; + +logic[SEL_WIDTH-1:0] sel_rev; +logic first_id_vld_mid; +logic secondid_vld_mid; +logic[SEL_ID_WIDHT-1:0] first_id_mid; +logic[SEL_ID_WIDHT-1:0] second_id_mid, second_id_mid_comp; + + + +generate + for(i = 0; i < SEL_WIDTH; i++) begin: sel_rev_gen + assign sel_rev[i] = sel_i[SEL_WIDTH-1-i]; + end +endgenerate + +priority_encoder +#( + .SEL_WIDTH (SEL_WIDTH) +) +first_vld_sel_u +( + .sel_i (sel_i ), + .id_vld_o (first_id_vld_mid ), + .id_o (first_id_mid ) +); + +priority_encoder +#( + .SEL_WIDTH (SEL_WIDTH) +) +second_vld_sel_u +( + .sel_i (sel_rev ), + .id_vld_o (secondid_vld_mid ), + .id_o (second_id_mid ) +); +assign second_id_mid_comp = SEL_ID_WIDHT'(SEL_WIDTH-1-second_id_mid); + +assign first_id_vld_o = first_id_vld_mid & first_id_needed_vld_i; +assign second_id_vld_o = secondid_vld_mid & (~(first_id_mid == second_id_mid_comp) | ~first_id_needed_vld_i) & second_id_needed_vld_i; +assign first_id_o = first_id_mid; +assign second_id_o = second_id_mid_comp; + +endmodule +`endif \ No newline at end of file diff --git a/rtl/util/sp_fifo_dat_vld_output.sv b/rtl/util/sp_fifo_dat_vld_output.sv new file mode 100755 index 0000000..d848146 --- /dev/null +++ b/rtl/util/sp_fifo_dat_vld_output.sv @@ -0,0 +1,106 @@ +module sp_fifo_dat_vld_output +#( + parameter type payload_t = logic[3:0], + // parameter int unsigned 1 = 4, + // parameter int unsigned 1 = 4, + parameter int unsigned DEPTH = 16, + parameter int unsigned MUST_TAKEN_ALL = 1 +) +( + // Enqueue + input logic[1-1:0] enqueue_vld_i, + input payload_t[1-1:0] enqueue_payload_i, + output logic[1-1:0] enqueue_rdy_o, + // Dequeue + output logic[1-1:0] dequeue_vld_o, + output payload_t[1-1:0] dequeue_payload_o, + input logic[1-1:0] dequeue_rdy_i, + + // output data and valid + output payload_t[DEPTH-1:0] payload_dff, + output logic [DEPTH-1:0] payload_vld_dff, + + input logic flush_i, + + input clk, + input rst +); + + localparam int unsigned ENTRY_PTR_WIDTH = $clog2(DEPTH); + localparam int unsigned ENTRY_CNT_WIDTH = $clog2(DEPTH+1); + + logic[1-1:0][ENTRY_PTR_WIDTH-1:0] enq_ptr; + logic[1-1:0][ENTRY_PTR_WIDTH-1:0] deq_ptr; + logic[ENTRY_CNT_WIDTH-1:0] avail_cnt; + + // payload_t[DEPTH-1:0] payload_dff; + // logic [DEPTH-1:0] payload_vld_dff; + + + logic[1-1:0] enq_fire; + logic[1-1:0] deq_fire; + + assign enq_fire = enqueue_vld_i & enqueue_rdy_o; + assign deq_fire = dequeue_vld_o & dequeue_rdy_i; + + generate + for(genvar i = 0 ; i < 1; i++) begin + assign dequeue_payload_o[i] = payload_dff[deq_ptr[i]]; + assign dequeue_vld_o[i] = (DEPTH-avail_cnt) > i; + end + endgenerate + + generate + if(MUST_TAKEN_ALL) begin + assign enqueue_rdy_o = {1{avail_cnt >= 1}}; + end else begin + for(genvar i = 0; i < 1; i++) begin + assign enqueue_rdy_o[i] = avail_cnt > i; + end + end + endgenerate + + + always_ff@(posedge clk) begin : payload_dff_update + for(int i = 0; i < 1; i++) begin + if(enq_fire[i]) begin + payload_dff[enq_ptr[i]] <= enqueue_payload_i[i]; + end + end + end + + always_ff@(posedge clk) begin : payload_vld_dff_update + if(rst) begin + payload_vld_dff <= '0; + end else begin + for(int i = 0; i < 1; i++) begin + if(enq_fire[i]) begin + payload_vld_dff[enq_ptr[i]] <= 1'b1; + end + if(deq_fire[i]) begin + payload_vld_dff[deq_ptr[i]] <= 1'b0; + end + end + end + end + + usage_manager #( + .ENTRY_COUNT(DEPTH), + .ENQ_WIDTH(1), + .DEQ_WIDTH(1), + .FLAG_EN(0), + .INIT_IS_FULL(0), + .COMB_DEQ_EN(0), + .COMB_ENQ_EN(1) + ) u_usage_manager ( + .enq_fire_i(enq_fire), + .deq_fire_i(deq_fire), + .head_o(deq_ptr), + .tail_o(enq_ptr), + .avail_cnt_o(avail_cnt), + .flush_i(flush_i), + .clk(clk), + .rst(rst) + ); + +endmodule : sp_fifo_dat_vld_output diff --git a/rtl/util/usage_manager.sv b/rtl/util/usage_manager.sv new file mode 100755 index 0000000..890bbcd --- /dev/null +++ b/rtl/util/usage_manager.sv @@ -0,0 +1,274 @@ +`ifndef __USAGE_MANAGER_SV__ +`define __USAGE_MANAGER_SV__ + +module usage_manager #( + parameter int unsigned ENTRY_COUNT = 8, + parameter int unsigned ENQ_WIDTH = 2, + parameter int unsigned DEQ_WIDTH = 2, + parameter int unsigned FLAG_EN = 0, + parameter int unsigned INIT_IS_FULL = 0, + parameter int unsigned COMB_ENQ_EN = 0, + parameter int unsigned COMB_DEQ_EN = 0, + localparam int unsigned ENTRY_PTR_WIDTH = $clog2(ENTRY_COUNT), + localparam int unsigned ENTRY_TAG_WIDTH = ENTRY_PTR_WIDTH + FLAG_EN, + localparam int unsigned USAGE_CNT_WIDTH = $clog2(ENTRY_COUNT + 1) +) ( + // Enqueue + input logic [ENQ_WIDTH-1:0] enq_fire_i, + + // Dequeue + input logic [DEQ_WIDTH-1:0] deq_fire_i, + + // Status + output logic [DEQ_WIDTH-1:0][ENTRY_TAG_WIDTH-1:0] head_o, + output logic [ENQ_WIDTH-1:0][ENTRY_TAG_WIDTH-1:0] tail_o, + output logic [USAGE_CNT_WIDTH-1:0] avail_cnt_o, + + input logic flush_i, + + input clk, + input rst +); + // Local Param + localparam int unsigned ENQ_CNT_WIDTH = $clog2(ENQ_WIDTH + 1); + localparam int unsigned DEQ_CNT_WIDTH = $clog2(DEQ_WIDTH + 1); + localparam int unsigned IS_2N = (2 ** ENTRY_PTR_WIDTH == ENTRY_COUNT); + // Function + function automatic [ENTRY_TAG_WIDTH-1:0] head_ptr_plus; + input logic [ENTRY_TAG_WIDTH-1:0] head_ptr_i; + input logic [DEQ_CNT_WIDTH-1:0] plus_cnt_i; + + logic flag; + logic [ENTRY_PTR_WIDTH-1:0] index; + logic [ENTRY_PTR_WIDTH:0] sum; + logic [ENTRY_PTR_WIDTH:0] reverse_sum; + begin + if (IS_2N) begin + head_ptr_plus = head_ptr_i + plus_cnt_i; + end else begin + index = head_ptr_i[ENTRY_PTR_WIDTH-1:0]; + flag = head_ptr_i[ENTRY_TAG_WIDTH-1]; + sum = index + plus_cnt_i; + reverse_sum = sum - ENTRY_COUNT; + if (FLAG_EN) begin + if (~reverse_sum[ENTRY_PTR_WIDTH]) begin + head_ptr_plus = {~flag, reverse_sum[ENTRY_PTR_WIDTH-1:0]}; + end else begin + head_ptr_plus = {flag, sum[ENTRY_PTR_WIDTH-1:0]}; + end + end else begin + if (~reverse_sum[ENTRY_PTR_WIDTH]) begin + head_ptr_plus = reverse_sum[ENTRY_PTR_WIDTH-1:0]; + end else begin + head_ptr_plus = sum[ENTRY_PTR_WIDTH-1:0]; + end + end + end + end + endfunction : head_ptr_plus + + + function automatic [ENTRY_TAG_WIDTH-1:0] ptr_plus_one; + input logic [ENTRY_TAG_WIDTH-1:0] ptr_i; + + logic flag; + logic [ENTRY_PTR_WIDTH-1:0] index; + logic reverse_flag; + begin + if (IS_2N) begin + ptr_plus_one = ptr_i + 1'b1; + end else begin + index = ptr_i[ENTRY_PTR_WIDTH-1:0]; + flag = ptr_i[ENTRY_TAG_WIDTH-1]; + if (index == ENTRY_COUNT - 1) begin + index = {ENTRY_PTR_WIDTH{1'b0}}; + reverse_flag = ~flag; + end else begin + index = ptr_i + 1'b1; + reverse_flag = flag; + end + if (FLAG_EN) begin + ptr_plus_one = {reverse_flag, index}; + end else begin + ptr_plus_one = index; + end + end + end + endfunction : ptr_plus_one + + function automatic [ENTRY_TAG_WIDTH-1:0] tail_ptr_plus; + input logic [ENTRY_TAG_WIDTH-1:0] tail_ptr_i; + input logic [ENQ_CNT_WIDTH-1:0] plus_cnt_i; + logic flag; + logic [ENTRY_PTR_WIDTH-1:0] index; + logic [ENTRY_PTR_WIDTH:0] sum; + logic [ENTRY_PTR_WIDTH:0] reverse_sum; + begin + if (IS_2N) begin + tail_ptr_plus = tail_ptr_i + plus_cnt_i; + end else begin + index = tail_ptr_i[ENTRY_PTR_WIDTH-1:0]; + flag = tail_ptr_i[ENTRY_TAG_WIDTH-1]; + sum = index + plus_cnt_i; + reverse_sum = sum - ENTRY_COUNT; + if (FLAG_EN) begin + if (~reverse_sum[ENTRY_PTR_WIDTH]) begin + tail_ptr_plus = {~flag, reverse_sum[ENTRY_PTR_WIDTH-1:0]}; + end else begin + tail_ptr_plus = {flag, sum[ENTRY_PTR_WIDTH-1:0]}; + end + end else begin + if (~reverse_sum[ENTRY_PTR_WIDTH]) begin + tail_ptr_plus = reverse_sum[ENTRY_PTR_WIDTH-1:0]; + end else begin + tail_ptr_plus = sum[ENTRY_PTR_WIDTH-1:0]; + end + end + end + end + endfunction : tail_ptr_plus + + + // Clock Gate + logic enq_clk_en, deq_clk_en; + + + logic [ENQ_CNT_WIDTH-1:0] enq_cnt; + logic [DEQ_CNT_WIDTH-1:0] deq_cnt; + logic [ENTRY_TAG_WIDTH-1:0] head_ptr_d, head_ptr_q; + logic [ENTRY_TAG_WIDTH-1:0] tail_ptr_d, tail_ptr_q; + logic [USAGE_CNT_WIDTH-1:0] avail_cnt_d, avail_cnt_q; + + + assign enq_clk_en = |enq_fire_i; + assign deq_clk_en = |deq_fire_i; + + // Output + always_comb begin : gen_head + for (int i = 0; i < DEQ_WIDTH; i++) begin + if (i == 0) begin + head_o[i] = head_ptr_q; + end else begin + if(COMB_DEQ_EN) begin + head_o[i] = deq_fire_i[i-1] ? ptr_plus_one(head_o[i-1]) : head_o[i-1]; + end else begin + head_o[i] = ptr_plus_one(head_o[i-1]); + end + end + end + end + always_comb begin : gen_tail + for (int i = 0; i < ENQ_WIDTH; i++) begin + if (i == 0) begin + tail_o[i] = tail_ptr_q; + end else begin + if(COMB_ENQ_EN) begin + tail_o[i] = enq_fire_i[i-1] ? ptr_plus_one(tail_o[i-1]) : tail_o[i-1]; + end else begin + tail_o[i] = ptr_plus_one(tail_o[i-1]); + end + end + end + end + assign avail_cnt_o = avail_cnt_q; + + always_comb begin : head_ptr_update + head_ptr_d = head_ptr_q; + if (deq_clk_en) begin + head_ptr_d = head_ptr_plus(head_ptr_q, deq_cnt); + end + if (flush_i) begin + head_ptr_d = head_ptr_q; + end + end + + always_comb begin : tail_ptr_update + tail_ptr_d = tail_ptr_q; + if (enq_clk_en) begin + tail_ptr_d = tail_ptr_plus(tail_ptr_q, enq_cnt); + end + if (flush_i) begin + if((INIT_IS_FULL==1) && (FLAG_EN==1)) begin + tail_ptr_d = {~head_ptr_q[ENTRY_TAG_WIDTH-1], head_ptr_q[ENTRY_PTR_WIDTH-1:0]}; + end else begin + tail_ptr_d = head_ptr_q; + end + end + end + + always_comb begin : avail_cnt_update + avail_cnt_d = avail_cnt_q; + if (enq_clk_en) begin + avail_cnt_d = avail_cnt_q - enq_cnt; + end + if (deq_clk_en) begin + avail_cnt_d = avail_cnt_q + deq_cnt; + end + if (enq_clk_en & deq_clk_en) begin + avail_cnt_d = avail_cnt_q + deq_cnt - enq_cnt; + end + if (flush_i) begin + if (INIT_IS_FULL) begin + avail_cnt_d = {USAGE_CNT_WIDTH{1'b0}}; + end else begin + avail_cnt_d = ENTRY_COUNT[USAGE_CNT_WIDTH-1:0]; + end + end + end + + always_ff @(posedge clk) begin : head_ptr_dff + if (rst) begin + head_ptr_q <= {ENTRY_TAG_WIDTH{1'b0}}; + end else begin + if (deq_clk_en | flush_i) begin + head_ptr_q <= head_ptr_d; + end + end + end + + always_ff @(posedge clk) begin : tail_ptr_dff + if (rst) begin + if (INIT_IS_FULL & FLAG_EN) begin + tail_ptr_q <= {1'b1, {ENTRY_PTR_WIDTH{1'b0}}}; + end else begin + tail_ptr_q <= {ENTRY_TAG_WIDTH{1'b0}}; + end + end else begin + if (enq_clk_en | flush_i) begin + tail_ptr_q <= tail_ptr_d; + end + end + end + + always_ff @(posedge clk) begin : avail_cnt_dff + if (rst) begin + if (INIT_IS_FULL) begin + avail_cnt_q <= {USAGE_CNT_WIDTH{1'b0}}; + end else begin + avail_cnt_q <= ENTRY_COUNT[USAGE_CNT_WIDTH-1:0]; + end + end else begin + if (enq_clk_en | deq_clk_en | flush_i) begin + avail_cnt_q <= avail_cnt_d; + end + end + end + + + one_counter #( + .DATA_WIDTH(ENQ_WIDTH) + ) u_enq_one_counter ( + .data_i(enq_fire_i), + .cnt_o (enq_cnt) + ); + + one_counter #( + .DATA_WIDTH(DEQ_WIDTH) + ) u_deq_one_counter ( + .data_i(deq_fire_i), + .cnt_o (deq_cnt) + ); + +endmodule : usage_manager + +`endif \ No newline at end of file diff --git a/rtl/vnet_router.sv b/rtl/vnet_router.sv new file mode 100644 index 0000000..46f5850 --- /dev/null +++ b/rtl/vnet_router.sv @@ -0,0 +1,1593 @@ +module vnet_router +import rvh_noc_pkg::*; +#( + parameter INPUT_PORT_NUM = 5, + parameter OUTPUT_PORT_NUM = 5, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + parameter type flit_payload_t = logic[256-1:0], + + parameter QOS_VC_NUM_PER_INPUT = 0, + + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter VC_NUM_INPUT_L = 4+LOCAL_PORT_NUM-1+QOS_VC_NUM_PER_INPUT, +`else + parameter VC_NUM_INPUT_L = 4+QOS_VC_NUM_PER_INPUT, +`endif + parameter VC_NUM_INPUT_N_IDX_W = VC_NUM_INPUT_N > 1 ? $clog2(VC_NUM_INPUT_N) : 1, + parameter VC_NUM_INPUT_S_IDX_W = VC_NUM_INPUT_S > 1 ? $clog2(VC_NUM_INPUT_S) : 1, + parameter VC_NUM_INPUT_E_IDX_W = VC_NUM_INPUT_E > 1 ? $clog2(VC_NUM_INPUT_E) : 1, + parameter VC_NUM_INPUT_W_IDX_W = VC_NUM_INPUT_W > 1 ? $clog2(VC_NUM_INPUT_W) : 1, + parameter VC_NUM_INPUT_L_IDX_W = VC_NUM_INPUT_L > 1 ? $clog2(VC_NUM_INPUT_L) : 1, + + parameter SA_GLOBAL_INPUT_NUM_N = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_S = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_E = 1+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_W = 1+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter SA_GLOBAL_INPUT_NUM_L = 4+LOCAL_PORT_NUM-1, +`else + parameter SA_GLOBAL_INPUT_NUM_L = 4, +`endif + parameter SA_GLOBAL_INPUT_NUM_N_IDX_W = SA_GLOBAL_INPUT_NUM_N > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_N) : 1, + parameter SA_GLOBAL_INPUT_NUM_S_IDX_W = SA_GLOBAL_INPUT_NUM_S > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_S) : 1, + parameter SA_GLOBAL_INPUT_NUM_E_IDX_W = SA_GLOBAL_INPUT_NUM_E > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_E) : 1, + parameter SA_GLOBAL_INPUT_NUM_W_IDX_W = SA_GLOBAL_INPUT_NUM_W > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_W) : 1, + parameter SA_GLOBAL_INPUT_NUM_L_IDX_W = SA_GLOBAL_INPUT_NUM_L > 1 ? $clog2(SA_GLOBAL_INPUT_NUM_L) : 1, + + parameter VC_NUM_OUTPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_L = 1, // vc number in local node + parameter VC_NUM_OUTPUT_N_IDX_W = VC_NUM_OUTPUT_N > 1 ? $clog2(VC_NUM_OUTPUT_N) : 1, + parameter VC_NUM_OUTPUT_S_IDX_W = VC_NUM_OUTPUT_S > 1 ? $clog2(VC_NUM_OUTPUT_S) : 1, + parameter VC_NUM_OUTPUT_E_IDX_W = VC_NUM_OUTPUT_E > 1 ? $clog2(VC_NUM_OUTPUT_E) : 1, + parameter VC_NUM_OUTPUT_W_IDX_W = VC_NUM_OUTPUT_W > 1 ? $clog2(VC_NUM_OUTPUT_W) : 1, + parameter VC_NUM_OUTPUT_L_IDX_W = VC_NUM_OUTPUT_L > 1 ? $clog2(VC_NUM_OUTPUT_L) : 1, + + parameter VC_DEPTH_INPUT_N = 2, + parameter VC_DEPTH_INPUT_S = 2, + parameter VC_DEPTH_INPUT_E = 2, + parameter VC_DEPTH_INPUT_W = 2, + parameter VC_DEPTH_INPUT_L = 2, + + parameter VC_DEPTH_OUTPUT_N = VC_DEPTH_INPUT_N, + parameter VC_DEPTH_OUTPUT_S = VC_DEPTH_INPUT_S, + parameter VC_DEPTH_OUTPUT_E = VC_DEPTH_INPUT_E, + parameter VC_DEPTH_OUTPUT_W = VC_DEPTH_INPUT_W, + parameter VC_DEPTH_OUTPUT_L = VC_DEPTH_INPUT_L, + parameter VC_DEPTH_OUTPUT_N_COUNTER_W = $clog2(VC_DEPTH_OUTPUT_N + 1), + parameter VC_DEPTH_OUTPUT_S_COUNTER_W = $clog2(VC_DEPTH_OUTPUT_S + 1), + parameter VC_DEPTH_OUTPUT_E_COUNTER_W = $clog2(VC_DEPTH_OUTPUT_E + 1), + parameter VC_DEPTH_OUTPUT_W_COUNTER_W = $clog2(VC_DEPTH_OUTPUT_W + 1), + parameter VC_DEPTH_OUTPUT_L_COUNTER_W = $clog2(VC_DEPTH_OUTPUT_L + 1) +) +( + // input from other router or local port // N,S,E,W,L + input logic [INPUT_PORT_NUM-1:0] rx_flit_pend_i, + input logic [INPUT_PORT_NUM-1:0] rx_flit_v_i, + input flit_payload_t [INPUT_PORT_NUM-1:0] rx_flit_i, + input logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id_i, + input io_port_t [INPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing_i, + + // output to other router or local port // N,S,E,W,L + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_pend_o, + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_v_o, + output flit_payload_t [OUTPUT_PORT_NUM-1:0] tx_flit_o, + output logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o, + output io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing_o, + + // free vc credit sent to sender + output logic [INPUT_PORT_NUM-1:0] rx_lcrd_v_o, + output logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o, + + // free vc credit received from receiver + input logic [OUTPUT_PORT_NUM-1:0] tx_lcrd_v_i, + input logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i, + + // router addr + input logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i, + input logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i, + + input logic clk, + input logic rstn +); + +genvar i, j, k; + +logic [INPUT_PORT_NUM-1:0] inport_read_enable_sa_stage; +// io_port_t [INPUT_PORT_NUM-1:0] inport_read_outport_id_sa_stage; +logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] inport_read_vc_id_sa_stage; +// io_port_t [INPUT_PORT_NUM-1:0] inport_look_ahead_routing_sa_stage; + +logic [OUTPUT_PORT_NUM-1:0] outport_vld_sa_stage; +io_port_t [OUTPUT_PORT_NUM-1:0] outport_select_inport_id_sa_stage; +logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] outport_vc_id_sa_stage; +io_port_t [OUTPUT_PORT_NUM-1:0] outport_look_ahead_routing_sa_stage; + +logic [OUTPUT_PORT_NUM-1:0] consume_vc_credit_vld; +logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] consume_vc_credit_vc_id; + +logic [INPUT_PORT_NUM-1:0] inport_read_enable_st_stage; +// io_port_t [INPUT_PORT_NUM-1:0] inport_read_outport_id_st_stage; +logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] inport_read_vc_id_st_stage; +// io_port_t [INPUT_PORT_NUM-1:0] inport_look_ahead_routing_st_stage; + +logic [OUTPUT_PORT_NUM-1:0] outport_vld_st_stage; +io_port_t [OUTPUT_PORT_NUM-1:0] outport_select_inport_id_st_stage; +logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] outport_vc_id_st_stage; +io_port_t [OUTPUT_PORT_NUM-1:0] outport_look_ahead_routing_st_stage; + +logic [OUTPUT_PORT_NUM-1:0] vc_assignment_vld; +logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] vc_assignment_vc_id; +io_port_t [OUTPUT_PORT_NUM-1:0] look_ahead_routing_sel; + +logic [INPUT_PORT_NUM-1:0][OUTPUT_PORT_NUMBER-1:0] sa_local_vld_to_sa_global; +logic [INPUT_PORT_NUM-1:0] sa_local_vld; +logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id; +logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX-1:0] sa_local_vc_id_oh; +`ifdef USE_QOS_VALUE +logic [INPUT_PORT_NUM-1:0][QoS_Value_Width-1:0] sa_local_qos_value; +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM +dpram_used_idx_t [INPUT_PORT_NUM-1:0] sa_local_dpram_idx; +`endif + +// ============= +// 1 input port +// ============= + +logic [VC_NUM_INPUT_N-1:0] vc_ctrl_head_vld_N; +flit_dec_t [VC_NUM_INPUT_N-1:0] vc_ctrl_head_N; +logic [VC_NUM_INPUT_S-1:0] vc_ctrl_head_vld_S; +flit_dec_t [VC_NUM_INPUT_S-1:0] vc_ctrl_head_S; +logic [VC_NUM_INPUT_E-1:0] vc_ctrl_head_vld_E; +flit_dec_t [VC_NUM_INPUT_E-1:0] vc_ctrl_head_E; +logic [VC_NUM_INPUT_W-1:0] vc_ctrl_head_vld_W; +flit_dec_t [VC_NUM_INPUT_W-1:0] vc_ctrl_head_W; + +flit_payload_t [VC_NUM_INPUT_N-1:0] vc_data_head_N; +flit_payload_t [VC_NUM_INPUT_S-1:0] vc_data_head_S; +flit_payload_t [VC_NUM_INPUT_E-1:0] vc_data_head_E; +flit_payload_t [VC_NUM_INPUT_W-1:0] vc_data_head_W; + +`ifdef HAVE_LOCAL_PORT +logic [LOCAL_PORT_NUM-1:0][VC_NUM_INPUT_L-1:0] vc_ctrl_head_vld_L; +flit_dec_t [LOCAL_PORT_NUM-1:0][VC_NUM_INPUT_L-1:0] vc_ctrl_head_L; +flit_payload_t [LOCAL_PORT_NUM-1:0][VC_NUM_INPUT_L-1:0] vc_data_head_L; +`endif + + + +input_port +#( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM_INPUT_N ), + .VC_DEPTH (VC_DEPTH_INPUT_N ), + + .INPUT_PORT_NO (0) +) +input_port_fromN_u +( + // input from other router or local port + .rx_flit_pend_i (rx_flit_pend_i [0]), + .rx_flit_v_i (rx_flit_v_i [0]), + .rx_flit_i (rx_flit_i [0]), + .rx_flit_vc_id_i (rx_flit_vc_id_i [0][VC_NUM_INPUT_N_IDX_W-1:0]), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i [0]), + + // free vc credit sent to sender + .rx_lcrd_v_o (rx_lcrd_v_o [0]), + .rx_lcrd_id_o (rx_lcrd_id_o [0]), + + // output head flit ctrl info to SA & RC unit + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_N ), + .vc_ctrl_head_o (vc_ctrl_head_N ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_N ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [0]), + .inport_read_vc_id_sa_stage_i (sa_local_vc_id [0][VC_NUM_INPUT_N_IDX_W-1:0]), // use sa_local_vc_id instead inport_read_vc_id_sa_stage to remove it from critical path +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (sa_local_dpram_idx [0]), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage [0]), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage [0][VC_NUM_INPUT_N_IDX_W-1:0]), + +`ifndef SYNTHESIS + // router addr + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), +`endif + + .clk (clk ), + .rstn (rstn ) +); + +input_port +#( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM_INPUT_S ), + .VC_DEPTH (VC_DEPTH_INPUT_S ), + + .INPUT_PORT_NO (1) +) +input_port_fromS_u +( + // input from other router or local port + .rx_flit_pend_i (rx_flit_pend_i [1]), + .rx_flit_v_i (rx_flit_v_i [1]), + .rx_flit_i (rx_flit_i [1]), + .rx_flit_vc_id_i (rx_flit_vc_id_i [1][VC_NUM_INPUT_S_IDX_W-1:0]), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i [1]), + + // free vc credit sent to sender + .rx_lcrd_v_o (rx_lcrd_v_o [1]), + .rx_lcrd_id_o (rx_lcrd_id_o [1]), + + // output head flit ctrl info to SA & RC unit + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_S ), + .vc_ctrl_head_o (vc_ctrl_head_S ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_S ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [1]), + .inport_read_vc_id_sa_stage_i (sa_local_vc_id [1][VC_NUM_INPUT_S_IDX_W-1:0]), // use sa_local_vc_id instead inport_read_vc_id_sa_stage to remove it from critical path +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (sa_local_dpram_idx [1]), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage [1]), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage [1][VC_NUM_INPUT_S_IDX_W-1:0]), + +`ifndef SYNTHESIS + // router addr + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), +`endif + + .clk (clk ), + .rstn (rstn ) +); + +input_port +#( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM_INPUT_E ), + .VC_DEPTH (VC_DEPTH_INPUT_E ), + + .INPUT_PORT_NO (2) +) +input_port_fromE_u +( + // input from other router or local port + .rx_flit_pend_i (rx_flit_pend_i [2]), + .rx_flit_v_i (rx_flit_v_i [2]), + .rx_flit_i (rx_flit_i [2]), + .rx_flit_vc_id_i (rx_flit_vc_id_i [2][VC_NUM_INPUT_E_IDX_W-1:0]), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i [2]), + + // free vc credit sent to sender + .rx_lcrd_v_o (rx_lcrd_v_o [2]), + .rx_lcrd_id_o (rx_lcrd_id_o [2]), + + // output head flit ctrl info to SA & RC unit + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_E ), + .vc_ctrl_head_o (vc_ctrl_head_E ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_E ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [2]), + .inport_read_vc_id_sa_stage_i (sa_local_vc_id [2][VC_NUM_INPUT_E_IDX_W-1:0]), // use sa_local_vc_id instead inport_read_vc_id_sa_stage to remove it from critical path +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (sa_local_dpram_idx [2]), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage [2]), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage [2][VC_NUM_INPUT_E_IDX_W-1:0]), + +`ifndef SYNTHESIS + // router addr + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), +`endif + + .clk (clk ), + .rstn (rstn ) +); + +input_port +#( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM_INPUT_W ), + .VC_DEPTH (VC_DEPTH_INPUT_W ), + + .INPUT_PORT_NO (3) +) +input_port_fromW_u +( + // input from other router or local port + .rx_flit_pend_i (rx_flit_pend_i [3]), + .rx_flit_v_i (rx_flit_v_i [3]), + .rx_flit_i (rx_flit_i [3]), + .rx_flit_vc_id_i (rx_flit_vc_id_i [3][VC_NUM_INPUT_W_IDX_W-1:0]), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i [3]), + + // free vc credit sent to sender + .rx_lcrd_v_o (rx_lcrd_v_o [3]), + .rx_lcrd_id_o (rx_lcrd_id_o [3]), + + // output head flit ctrl info to SA & RC unit + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_W ), + .vc_ctrl_head_o (vc_ctrl_head_W ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_W ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [3]), + .inport_read_vc_id_sa_stage_i (sa_local_vc_id [3][VC_NUM_INPUT_W_IDX_W-1:0]), // use sa_local_vc_id instead inport_read_vc_id_sa_stage to remove it from critical path +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (sa_local_dpram_idx [3]), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage [3]), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage [3][VC_NUM_INPUT_W_IDX_W-1:0]), + +`ifndef SYNTHESIS + // router addr + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), +`endif + + .clk (clk ), + .rstn (rstn ) +); + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_input_port_fromL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_input_port_fromL + input_port + #( + .flit_payload_t (flit_payload_t ), + .VC_NUM (VC_NUM_INPUT_L ), + .VC_DEPTH (VC_DEPTH_INPUT_L ), + + .INPUT_PORT_NO (4+i) + ) + input_port_fromL_u + ( + // input from other router or local port + .rx_flit_pend_i (rx_flit_pend_i [4+i] ), + .rx_flit_v_i (rx_flit_v_i [4+i] ), + .rx_flit_i (rx_flit_i [4+i] ), + .rx_flit_vc_id_i (rx_flit_vc_id_i [4+i][VC_NUM_INPUT_L_IDX_W-1:0] ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i [4+i] ), + + // free vc credit sent to sender + .rx_lcrd_v_o (rx_lcrd_v_o [4+i] ), + .rx_lcrd_id_o (rx_lcrd_id_o [4+i] ), + + // output head flit ctrl info to SA & RC unit + .vc_ctrl_head_vld_o (vc_ctrl_head_vld_L [i] ), + .vc_ctrl_head_o (vc_ctrl_head_L [i] ), + + // output data to switch traversal + .vc_data_head_o (vc_data_head_L [i] ), + + // input pop flit ctrl fifo (comes from SA stage) + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [4+i]), + .inport_read_vc_id_sa_stage_i (sa_local_vc_id [4+i][VC_NUM_INPUT_L_IDX_W-1:0]), // use sa_local_vc_id instead inport_read_vc_id_sa_stage to remove it from critical path +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .inport_read_dpram_idx_i (sa_local_dpram_idx [4+i]), +`endif + + // input pop flit ctrl fifo (comes from ST stage) + .inport_read_enable_st_stage_i (inport_read_enable_st_stage [4+i]), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage [4+i][VC_NUM_INPUT_L_IDX_W-1:0]), + +`ifndef SYNTHESIS + // router addr + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), +`endif + + .clk (clk ), + .rstn (rstn ) + ); + end + end +endgenerate + +// ========= +// local sa +// ========= + +sa_local +#( + .INPUT_NUM(VC_NUM_INPUT_N ) +) +sa_local_fromN_u ( + .vc_ctrl_head_vld_i (vc_ctrl_head_vld_N ), + .vc_ctrl_head_i (vc_ctrl_head_N ), + + .sa_local_vld_to_sa_global_o (sa_local_vld_to_sa_global [0]), + .sa_local_vld_o (sa_local_vld [0]), + .sa_local_vc_id_o (sa_local_vc_id [0][VC_NUM_INPUT_N_IDX_W-1:0]), + .sa_local_vc_id_oh_o (sa_local_vc_id_oh [0][VC_NUM_INPUT_N-1:0]), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_o (sa_local_qos_value [0]), +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .sa_local_dpram_idx_o (sa_local_dpram_idx [0]), +`endif + + + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [0]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(VC_ID_NUM_MAX_W > VC_NUM_INPUT_N_IDX_W) begin + assign sa_local_vc_id[0][VC_ID_NUM_MAX_W-1:VC_NUM_INPUT_N_IDX_W] = '0; + end +endgenerate + +sa_local +#( + .INPUT_NUM(VC_NUM_INPUT_S ) +) +sa_local_fromS_u ( + .vc_ctrl_head_vld_i (vc_ctrl_head_vld_S ), + .vc_ctrl_head_i (vc_ctrl_head_S ), + + .sa_local_vld_to_sa_global_o (sa_local_vld_to_sa_global [1]), + .sa_local_vld_o (sa_local_vld [1]), + .sa_local_vc_id_o (sa_local_vc_id [1][VC_NUM_INPUT_S_IDX_W-1:0]), + .sa_local_vc_id_oh_o (sa_local_vc_id_oh [1][VC_NUM_INPUT_S-1:0]), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_o (sa_local_qos_value [1]), +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .sa_local_dpram_idx_o (sa_local_dpram_idx [1]), +`endif + + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [1]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(VC_ID_NUM_MAX_W > VC_NUM_INPUT_S_IDX_W) begin + assign sa_local_vc_id[1][VC_ID_NUM_MAX_W-1:VC_NUM_INPUT_S_IDX_W] = '0; + end +endgenerate + +sa_local +#( + .INPUT_NUM(VC_NUM_INPUT_E ) +) +sa_local_fromE_u ( + .vc_ctrl_head_vld_i (vc_ctrl_head_vld_E ), + .vc_ctrl_head_i (vc_ctrl_head_E ), + + .sa_local_vld_to_sa_global_o (sa_local_vld_to_sa_global [2]), + .sa_local_vld_o (sa_local_vld [2]), + .sa_local_vc_id_o (sa_local_vc_id [2][VC_NUM_INPUT_E_IDX_W-1:0]), + .sa_local_vc_id_oh_o (sa_local_vc_id_oh [2][VC_NUM_INPUT_E-1:0]), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_o (sa_local_qos_value [2]), +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .sa_local_dpram_idx_o (sa_local_dpram_idx [2]), +`endif + + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [2]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(VC_ID_NUM_MAX_W > VC_NUM_INPUT_E_IDX_W) begin + assign sa_local_vc_id[2][VC_ID_NUM_MAX_W-1:VC_NUM_INPUT_E_IDX_W] = '0; + end +endgenerate + +sa_local +#( + .INPUT_NUM(VC_NUM_INPUT_W ) +) +sa_local_fromW_u ( + .vc_ctrl_head_vld_i (vc_ctrl_head_vld_W ), + .vc_ctrl_head_i (vc_ctrl_head_W ), + + .sa_local_vld_to_sa_global_o (sa_local_vld_to_sa_global [3]), + .sa_local_vld_o (sa_local_vld [3]), + .sa_local_vc_id_o (sa_local_vc_id [3][VC_NUM_INPUT_W_IDX_W-1:0]), + .sa_local_vc_id_oh_o (sa_local_vc_id_oh [3][VC_NUM_INPUT_W-1:0]), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_o (sa_local_qos_value [3]), +`endif +`ifdef VC_DATA_USE_DUAL_PORT_RAM + .sa_local_dpram_idx_o (sa_local_dpram_idx [3]), +`endif + + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [3]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(VC_ID_NUM_MAX_W > VC_NUM_INPUT_W_IDX_W) begin + assign sa_local_vc_id[3][VC_ID_NUM_MAX_W-1:VC_NUM_INPUT_W_IDX_W] = '0; + end +endgenerate + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_sa_local_fromL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_sa_local_fromL + sa_local + #( + .INPUT_NUM(VC_NUM_INPUT_L ) + ) + sa_local_fromL_u ( + .vc_ctrl_head_vld_i (vc_ctrl_head_vld_L [i]), + .vc_ctrl_head_i (vc_ctrl_head_L [i]), + + .sa_local_vld_to_sa_global_o (sa_local_vld_to_sa_global [4+i]), + .sa_local_vld_o (sa_local_vld [4+i]), + .sa_local_vc_id_o (sa_local_vc_id [4+i][VC_NUM_INPUT_L_IDX_W-1:0]), + .sa_local_vc_id_oh_o (sa_local_vc_id_oh [4+i][VC_NUM_INPUT_L-1:0]), + `ifdef USE_QOS_VALUE + .sa_local_qos_value_o (sa_local_qos_value [4+i]), + `endif + `ifdef VC_DATA_USE_DUAL_PORT_RAM + .sa_local_dpram_idx_o (sa_local_dpram_idx [4+i]), + `endif + + .inport_read_enable_sa_stage_i (inport_read_enable_sa_stage [4+i]), + + .clk (clk ), + .rstn (rstn) + ); + + if(VC_ID_NUM_MAX_W > VC_NUM_INPUT_L_IDX_W) begin + assign sa_local_vc_id[4+i][VC_ID_NUM_MAX_W-1:VC_NUM_INPUT_L_IDX_W] = '0; + end + end + end +endgenerate + +// ========== +// global sa +// ========== + +logic [OUTPUT_PORT_NUM-1:0] sa_global_vld; +logic [OUTPUT_PORT_NUM-1:0][QoS_Value_Width-1:0] sa_global_qos_value; +logic [OUTPUT_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_MAX-1:0] sa_global_inport_id_oh; +logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] sa_global_inport_vc_id; + +logic [SA_GLOBAL_INPUT_NUM_N-1:0] sa_local_vld_to_sa_global_all_inport_toN; +logic [SA_GLOBAL_INPUT_NUM_N-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_all_inport_toN; +logic [SA_GLOBAL_INPUT_NUM_S-1:0] sa_local_vld_to_sa_global_all_inport_toS; +logic [SA_GLOBAL_INPUT_NUM_S-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_all_inport_toS; +logic [SA_GLOBAL_INPUT_NUM_E-1:0] sa_local_vld_to_sa_global_all_inport_toE; +logic [SA_GLOBAL_INPUT_NUM_E-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_all_inport_toE; +logic [SA_GLOBAL_INPUT_NUM_W-1:0] sa_local_vld_to_sa_global_all_inport_toW; +logic [SA_GLOBAL_INPUT_NUM_W-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_all_inport_toW; +`ifdef HAVE_LOCAL_PORT +logic [LOCAL_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_L-1:0] sa_local_vld_to_sa_global_all_inport_toL; +logic [LOCAL_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_L-1:0][VC_ID_NUM_MAX_W-1:0] sa_local_vc_id_all_inport_toL; + `ifdef USE_QOS_VALUE +logic [LOCAL_PORT_NUM-1:0][SA_GLOBAL_INPUT_NUM_L-1:0][QoS_Value_Width-1:0] sa_local_qos_value_all_inport_toL; + `endif +`endif + +`ifdef USE_QOS_VALUE +logic [SA_GLOBAL_INPUT_NUM_N-1:0][QoS_Value_Width-1:0] sa_local_qos_value_all_inport_toN; +logic [SA_GLOBAL_INPUT_NUM_S-1:0][QoS_Value_Width-1:0] sa_local_qos_value_all_inport_toS; +logic [SA_GLOBAL_INPUT_NUM_E-1:0][QoS_Value_Width-1:0] sa_local_qos_value_all_inport_toE; +logic [SA_GLOBAL_INPUT_NUM_W-1:0][QoS_Value_Width-1:0] sa_local_qos_value_all_inport_toW; +`endif + +assign sa_local_vld_to_sa_global_all_inport_toN[0] = sa_local_vld_to_sa_global[1][0]; +assign sa_local_vld_to_sa_global_all_inport_toN[1] = sa_local_vld_to_sa_global[2][0]; +assign sa_local_vld_to_sa_global_all_inport_toN[2] = sa_local_vld_to_sa_global[3][0]; +assign sa_local_vc_id_all_inport_toN [0] = sa_local_vc_id [1]; +assign sa_local_vc_id_all_inport_toN [1] = sa_local_vc_id [2]; +assign sa_local_vc_id_all_inport_toN [2] = sa_local_vc_id [3]; + +`ifdef USE_QOS_VALUE +assign sa_local_qos_value_all_inport_toN [0] = sa_local_qos_value [1]; +assign sa_local_qos_value_all_inport_toN [1] = sa_local_qos_value [2]; +assign sa_local_qos_value_all_inport_toN [2] = sa_local_qos_value [3]; +`endif + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_sa_local_vld_to_sa_global_all_inport_toN_fromL_signal + for(i = 0; i < LOCAL_PORT_NUM; i++) begin + assign sa_local_vld_to_sa_global_all_inport_toN[3+i] = sa_local_vld_to_sa_global[4+i][0]; + assign sa_local_vc_id_all_inport_toN [3+i] = sa_local_vc_id [4+i]; + `ifdef USE_QOS_VALUE + assign sa_local_qos_value_all_inport_toN [3+i] = sa_local_qos_value [4+i]; + `endif + end + end +endgenerate + + + +sa_global +#( + .INPUT_NUM (SA_GLOBAL_INPUT_NUM_N ) +) +sa_global_toN_u ( + + .sa_local_vld_i (sa_local_vld_to_sa_global_all_inport_toN ), + .sa_local_vc_id_i (sa_local_vc_id_all_inport_toN ), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_i (sa_local_qos_value_all_inport_toN ), +`endif + + .sa_global_vld_o (sa_global_vld [0]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_o (sa_global_qos_value [0]), +`endif + .sa_global_inport_id_oh_o (sa_global_inport_id_oh [0][SA_GLOBAL_INPUT_NUM_N-1:0]), + .sa_global_inport_vc_id_o (sa_global_inport_vc_id [0]), + + .vc_assignment_vld_i (vc_assignment_vld [0]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(SA_GLOBAL_INPUT_NUM_MAX > SA_GLOBAL_INPUT_NUM_N) begin + assign sa_global_inport_id_oh[0][SA_GLOBAL_INPUT_NUM_MAX-1:SA_GLOBAL_INPUT_NUM_N] = '0; + end +endgenerate + + +assign sa_local_vld_to_sa_global_all_inport_toS[0] = sa_local_vld_to_sa_global[0][1]; +assign sa_local_vld_to_sa_global_all_inport_toS[1] = sa_local_vld_to_sa_global[2][1]; +assign sa_local_vld_to_sa_global_all_inport_toS[2] = sa_local_vld_to_sa_global[3][1]; +assign sa_local_vc_id_all_inport_toS [0] = sa_local_vc_id [0]; +assign sa_local_vc_id_all_inport_toS [1] = sa_local_vc_id [2]; +assign sa_local_vc_id_all_inport_toS [2] = sa_local_vc_id [3]; + +`ifdef USE_QOS_VALUE +assign sa_local_qos_value_all_inport_toS [0] = sa_local_qos_value [0]; +assign sa_local_qos_value_all_inport_toS [1] = sa_local_qos_value [2]; +assign sa_local_qos_value_all_inport_toS [2] = sa_local_qos_value [3]; +`endif + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_sa_local_vld_to_sa_global_all_inport_toS_fromL_signal + for(i = 0; i < LOCAL_PORT_NUM; i++) begin + assign sa_local_vld_to_sa_global_all_inport_toS[3+i] = sa_local_vld_to_sa_global[4+i][1]; + assign sa_local_vc_id_all_inport_toS [3+i] = sa_local_vc_id [4+i]; + `ifdef USE_QOS_VALUE + assign sa_local_qos_value_all_inport_toS [3+i] = sa_local_qos_value [4+i]; + `endif + end + end +endgenerate + +sa_global +#( + .INPUT_NUM (SA_GLOBAL_INPUT_NUM_S ) +) +sa_global_toS_u ( + + .sa_local_vld_i (sa_local_vld_to_sa_global_all_inport_toS ), + .sa_local_vc_id_i (sa_local_vc_id_all_inport_toS ), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_i (sa_local_qos_value_all_inport_toS ), +`endif + + .sa_global_vld_o (sa_global_vld [1]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_o (sa_global_qos_value [1]), +`endif + .sa_global_inport_id_oh_o (sa_global_inport_id_oh [1][SA_GLOBAL_INPUT_NUM_S-1:0]), + .sa_global_inport_vc_id_o (sa_global_inport_vc_id [1]), + + .vc_assignment_vld_i (vc_assignment_vld [1]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(SA_GLOBAL_INPUT_NUM_MAX > SA_GLOBAL_INPUT_NUM_S) begin + assign sa_global_inport_id_oh[1][SA_GLOBAL_INPUT_NUM_MAX-1:SA_GLOBAL_INPUT_NUM_S] = '0; + end +endgenerate + + +assign sa_local_vld_to_sa_global_all_inport_toE[0] = sa_local_vld_to_sa_global[3][2]; +assign sa_local_vc_id_all_inport_toE [0] = sa_local_vc_id [3]; + +`ifdef USE_QOS_VALUE +assign sa_local_qos_value_all_inport_toE [0] = sa_local_qos_value [3]; +`endif + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_sa_local_vld_to_sa_global_all_inport_toE_fromL_signal + for(i = 0; i < LOCAL_PORT_NUM; i++) begin + assign sa_local_vld_to_sa_global_all_inport_toE[1+i] = sa_local_vld_to_sa_global[4+i][2]; + assign sa_local_vc_id_all_inport_toE [1+i] = sa_local_vc_id [4+i]; + `ifdef USE_QOS_VALUE + assign sa_local_qos_value_all_inport_toE [1+i] = sa_local_qos_value [4+i]; + `endif + end + end +endgenerate + +sa_global +#( + .INPUT_NUM (SA_GLOBAL_INPUT_NUM_E ) +) +sa_global_toE_u ( + + .sa_local_vld_i (sa_local_vld_to_sa_global_all_inport_toE ), + .sa_local_vc_id_i (sa_local_vc_id_all_inport_toE ), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_i (sa_local_qos_value_all_inport_toE ), +`endif + + .sa_global_vld_o (sa_global_vld [2]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_o (sa_global_qos_value [2]), +`endif + .sa_global_inport_id_oh_o (sa_global_inport_id_oh [2][SA_GLOBAL_INPUT_NUM_E-1:0]), + .sa_global_inport_vc_id_o (sa_global_inport_vc_id [2]), + + .vc_assignment_vld_i (vc_assignment_vld [2]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(SA_GLOBAL_INPUT_NUM_MAX > SA_GLOBAL_INPUT_NUM_E) begin + assign sa_global_inport_id_oh[2][SA_GLOBAL_INPUT_NUM_MAX-1:SA_GLOBAL_INPUT_NUM_E] = '0; + end +endgenerate + + + +assign sa_local_vld_to_sa_global_all_inport_toW[0] = sa_local_vld_to_sa_global[2][3]; +assign sa_local_vc_id_all_inport_toW [0] = sa_local_vc_id [2]; + +`ifdef USE_QOS_VALUE +assign sa_local_qos_value_all_inport_toW [0] = sa_local_qos_value [2]; +`endif + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_sa_local_vld_to_sa_global_all_inport_toW_fromL_signal + for(i = 0; i < LOCAL_PORT_NUM; i++) begin + assign sa_local_vld_to_sa_global_all_inport_toW[1+i] = sa_local_vld_to_sa_global[4+i][3]; + assign sa_local_vc_id_all_inport_toW [1+i] = sa_local_vc_id [4+i]; + `ifdef USE_QOS_VALUE + assign sa_local_qos_value_all_inport_toW [1+i] = sa_local_qos_value [4+i]; + `endif + end + end +endgenerate + +sa_global +#( + .INPUT_NUM (SA_GLOBAL_INPUT_NUM_W ) +) +sa_global_toW_u ( + + .sa_local_vld_i (sa_local_vld_to_sa_global_all_inport_toW ), + .sa_local_vc_id_i (sa_local_vc_id_all_inport_toW ), +`ifdef USE_QOS_VALUE + .sa_local_qos_value_i (sa_local_qos_value_all_inport_toW ), +`endif + + .sa_global_vld_o (sa_global_vld [3]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_o (sa_global_qos_value [3]), +`endif + .sa_global_inport_id_oh_o (sa_global_inport_id_oh [3][SA_GLOBAL_INPUT_NUM_W-1:0]), + .sa_global_inport_vc_id_o (sa_global_inport_vc_id [3]), + + .vc_assignment_vld_i (vc_assignment_vld [3]), + + .clk (clk ), + .rstn (rstn) +); + +generate + if(SA_GLOBAL_INPUT_NUM_MAX > SA_GLOBAL_INPUT_NUM_W) begin + assign sa_global_inport_id_oh[3][SA_GLOBAL_INPUT_NUM_MAX-1:SA_GLOBAL_INPUT_NUM_W] = '0; + end +endgenerate + + + +`ifdef HAVE_LOCAL_PORT + +always_comb begin + int k; + for(int i = 0; i < LOCAL_PORT_NUM; i++) begin + for(int j = 0; j < 4; j++) begin + sa_local_vld_to_sa_global_all_inport_toL[i][j] = sa_local_vld_to_sa_global[j][4+i]; + sa_local_vc_id_all_inport_toL [i][j] = sa_local_vc_id [j]; + `ifdef USE_QOS_VALUE + sa_local_qos_value_all_inport_toL [i][j] = sa_local_qos_value [j]; + `endif + end + `ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + k = 0; + for(int j = 0; j < LOCAL_PORT_NUM; j++) begin + if(i != j) begin + sa_local_vld_to_sa_global_all_inport_toL[i][4+k] = sa_local_vld_to_sa_global[4+j][4+i]; + sa_local_vc_id_all_inport_toL [i][4+k] = sa_local_vc_id [4+j]; + `ifdef USE_QOS_VALUE + sa_local_qos_value_all_inport_toL [i][4+k] = sa_local_qos_value [4+j]; + `endif + k++; + end + end + `endif + end +end + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_sa_global_toL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_sa_global_toL + sa_global + #( + .INPUT_NUM (SA_GLOBAL_INPUT_NUM_L ) + ) + sa_global_toL_u ( + + .sa_local_vld_i (sa_local_vld_to_sa_global_all_inport_toL [i] ), + .sa_local_vc_id_i (sa_local_vc_id_all_inport_toL [i] ), + `ifdef USE_QOS_VALUE + .sa_local_qos_value_i (sa_local_qos_value_all_inport_toL [i] ), + `endif + + .sa_global_vld_o (sa_global_vld [4+i]), + `ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_o (sa_global_qos_value [4+i]), + `endif + .sa_global_inport_id_oh_o (sa_global_inport_id_oh [4+i][SA_GLOBAL_INPUT_NUM_L-1:0]), + .sa_global_inport_vc_id_o (sa_global_inport_vc_id [4+i]), + + .vc_assignment_vld_i (vc_assignment_vld [4+i]), + + .clk (clk ), + .rstn (rstn) + ); + + + if(SA_GLOBAL_INPUT_NUM_MAX > SA_GLOBAL_INPUT_NUM_L) begin + assign sa_global_inport_id_oh[4+i][SA_GLOBAL_INPUT_NUM_MAX-1:SA_GLOBAL_INPUT_NUM_L] = '0; + end + end + end +endgenerate +`endif + +// =================== +// look-ahead routing +// =================== +io_port_t [INPUT_PORT_NUM-1:0] look_ahead_routing; +flit_dec_t [INPUT_PORT_NUM-1:0] vc_ctrl_head_sa_local_sel; + + +onehot_mux +#( + .SOURCE_COUNT(VC_NUM_INPUT_N ), + .DATA_WIDTH ($bits(flit_dec_t) ) +) +onehot_mux_vc_ctrl_head_sa_local_sel_N_u ( + .sel_i (sa_local_vc_id_oh[0][VC_NUM_INPUT_N-1:0] ), + .data_i (vc_ctrl_head_N ), + .data_o (vc_ctrl_head_sa_local_sel[0]) +); + +look_ahead_routing +#( +) +look_ahead_routing_fromN_u ( + .vc_ctrl_head_vld_i (sa_local_vld [0] ), + .vc_ctrl_head_i (vc_ctrl_head_sa_local_sel[0] ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .look_ahead_routing_o (look_ahead_routing [0]) +); + + +onehot_mux +#( + .SOURCE_COUNT(VC_NUM_INPUT_S ), + .DATA_WIDTH ($bits(flit_dec_t) ) +) +onehot_mux_vc_ctrl_head_sa_local_sel_S_u ( + .sel_i (sa_local_vc_id_oh[1][VC_NUM_INPUT_S-1:0] ), + .data_i (vc_ctrl_head_S ), + .data_o (vc_ctrl_head_sa_local_sel[1]) +); + +look_ahead_routing +#( +) +look_ahead_routing_fromS_u ( + .vc_ctrl_head_vld_i (sa_local_vld [1] ), + .vc_ctrl_head_i (vc_ctrl_head_sa_local_sel[1] ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .look_ahead_routing_o (look_ahead_routing [1]) +); + + +onehot_mux +#( + .SOURCE_COUNT(VC_NUM_INPUT_E ), + .DATA_WIDTH ($bits(flit_dec_t) ) +) +onehot_mux_vc_ctrl_head_sa_local_sel_E_u ( + .sel_i (sa_local_vc_id_oh[2][VC_NUM_INPUT_E-1:0] ), + .data_i (vc_ctrl_head_E ), + .data_o (vc_ctrl_head_sa_local_sel[2]) +); + +look_ahead_routing +#( +) +look_ahead_routing_fromE_u ( + .vc_ctrl_head_vld_i (sa_local_vld [2] ), + .vc_ctrl_head_i (vc_ctrl_head_sa_local_sel[2] ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .look_ahead_routing_o (look_ahead_routing [2]) +); + + +onehot_mux +#( + .SOURCE_COUNT(VC_NUM_INPUT_W ), + .DATA_WIDTH ($bits(flit_dec_t) ) +) +onehot_mux_vc_ctrl_head_sa_local_sel_W_u ( + .sel_i (sa_local_vc_id_oh[3][VC_NUM_INPUT_W-1:0] ), + .data_i (vc_ctrl_head_W ), + .data_o (vc_ctrl_head_sa_local_sel[3]) +); + +look_ahead_routing +#( +) +look_ahead_routing_fromW_u ( + .vc_ctrl_head_vld_i (sa_local_vld [3] ), + .vc_ctrl_head_i (vc_ctrl_head_sa_local_sel[3] ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .look_ahead_routing_o (look_ahead_routing [3]) +); + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_look_ahead_routing_fromL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_look_ahead_routing_fromL + onehot_mux + #( + .SOURCE_COUNT(VC_NUM_INPUT_L ), + .DATA_WIDTH ($bits(flit_dec_t) ) + ) + onehot_mux_look_ahead_routing_sel_u ( + .sel_i (sa_local_vc_id_oh[4+i][VC_NUM_INPUT_L-1:0] ), + .data_i (vc_ctrl_head_L[i] ), + .data_o (vc_ctrl_head_sa_local_sel[4+i]) + ); + + look_ahead_routing + #( + ) + look_ahead_routing_fromL_u ( + .vc_ctrl_head_vld_i (sa_local_vld [4+i] ), + .vc_ctrl_head_i (vc_ctrl_head_sa_local_sel[4+i] ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .look_ahead_routing_o (look_ahead_routing [4+i]) + ); + end + end +endgenerate + +// ============================== +// output port vc credit counter +// ============================== +logic [VC_NUM_OUTPUT_N-1:0][VC_DEPTH_OUTPUT_N_COUNTER_W-1:0] vc_credit_counter_toN; +logic [VC_NUM_OUTPUT_S-1:0][VC_DEPTH_OUTPUT_S_COUNTER_W-1:0] vc_credit_counter_toS; +logic [VC_NUM_OUTPUT_E-1:0][VC_DEPTH_OUTPUT_E_COUNTER_W-1:0] vc_credit_counter_toE; +logic [VC_NUM_OUTPUT_W-1:0][VC_DEPTH_OUTPUT_W_COUNTER_W-1:0] vc_credit_counter_toW; +`ifdef HAVE_LOCAL_PORT +logic [LOCAL_PORT_NUM-1:0][VC_NUM_OUTPUT_L-1:0][VC_DEPTH_OUTPUT_L_COUNTER_W-1:0] vc_credit_counter_toL; +`endif + +output_port_vc_credit_counter +#( + .VC_NUM (VC_NUM_OUTPUT_N ), + .VC_DEPTH (VC_DEPTH_OUTPUT_N ) +) +output_port_vc_credit_counter_toN_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i [0] ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i [0][VC_NUM_OUTPUT_N_IDX_W-1:0] ), + .consume_vc_credit_vld_i (consume_vc_credit_vld [0] ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id[0][VC_NUM_OUTPUT_N_IDX_W-1:0] ), + .vc_credit_counter_o (vc_credit_counter_toN ), + .clk (clk ), + .rstn (rstn ) +); + +output_port_vc_credit_counter +#( + .VC_NUM (VC_NUM_OUTPUT_S ), + .VC_DEPTH (VC_DEPTH_OUTPUT_S ) +) +output_port_vc_credit_counter_toS_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i [1] ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i [1][VC_NUM_OUTPUT_S_IDX_W-1:0] ), + .consume_vc_credit_vld_i (consume_vc_credit_vld [1] ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id[1][VC_NUM_OUTPUT_S_IDX_W-1:0] ), + .vc_credit_counter_o (vc_credit_counter_toS ), + .clk (clk ), + .rstn (rstn ) +); + +output_port_vc_credit_counter +#( + .VC_NUM (VC_NUM_OUTPUT_E ), + .VC_DEPTH (VC_DEPTH_OUTPUT_E ) +) +output_port_vc_credit_counter_toE_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i [2] ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i [2][VC_NUM_OUTPUT_E_IDX_W-1:0] ), + .consume_vc_credit_vld_i (consume_vc_credit_vld [2] ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id[2][VC_NUM_OUTPUT_E_IDX_W-1:0] ), + .vc_credit_counter_o (vc_credit_counter_toE ), + .clk (clk ), + .rstn (rstn ) +); + +output_port_vc_credit_counter +#( + .VC_NUM (VC_NUM_OUTPUT_W ), + .VC_DEPTH (VC_DEPTH_OUTPUT_W ) +) +output_port_vc_credit_counter_toW_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i [3] ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i [3][VC_NUM_OUTPUT_W_IDX_W-1:0] ), + .consume_vc_credit_vld_i (consume_vc_credit_vld [3] ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id[3][VC_NUM_OUTPUT_W_IDX_W-1:0] ), + .vc_credit_counter_o (vc_credit_counter_toW ), + .clk (clk ), + .rstn (rstn ) +); + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_output_port_vc_credit_counter_toL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_output_port_vc_credit_counter_toL + output_port_vc_credit_counter + #( + .VC_NUM (VC_NUM_OUTPUT_L ), + .VC_DEPTH (VC_DEPTH_OUTPUT_L ) + ) + output_port_vc_credit_counter_toL_u ( + .free_vc_credit_vld_i (tx_lcrd_v_i [4+i] ), + .free_vc_credit_vc_id_i (tx_lcrd_id_i [4+i][VC_NUM_OUTPUT_L_IDX_W-1:0] ), + .consume_vc_credit_vld_i (consume_vc_credit_vld [4+i] ), + .consume_vc_credit_vc_id_i (consume_vc_credit_vc_id[4+i][VC_NUM_OUTPUT_L_IDX_W-1:0] ), + .vc_credit_counter_o (vc_credit_counter_toL [i] ), + .clk (clk ), + .rstn (rstn ) + ); + end + end +endgenerate + +// ============= +// vc selection +// ============= + +vc_select_vld_t [VC_NUM_OUTPUT_N-1:0] vc_select_vld_toN; +vc_select_vc_id_t [VC_NUM_OUTPUT_N-1:0] vc_select_vc_id_toN; +vc_select_vld_t [VC_NUM_OUTPUT_S-1:0] vc_select_vld_toS; +vc_select_vc_id_t [VC_NUM_OUTPUT_S-1:0] vc_select_vc_id_toS; +vc_select_vld_t [VC_NUM_OUTPUT_E-1:0] vc_select_vld_toE; +vc_select_vc_id_t [VC_NUM_OUTPUT_E-1:0] vc_select_vc_id_toE; +vc_select_vld_t [VC_NUM_OUTPUT_W-1:0] vc_select_vld_toW; +vc_select_vc_id_t [VC_NUM_OUTPUT_W-1:0] vc_select_vc_id_toW; +`ifdef HAVE_LOCAL_PORT +vc_select_vld_t [LOCAL_PORT_NUM-1:0][VC_NUM_OUTPUT_L-1:0] vc_select_vld_toL; +vc_select_vc_id_t [LOCAL_PORT_NUM-1:0][VC_NUM_OUTPUT_L-1:0] vc_select_vc_id_toL; +`endif + +output_port_vc_selection +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_N ), + .OUTPUT_VC_DEPTH (VC_DEPTH_OUTPUT_N ) +) +output_port_vc_selection_toN_u ( + .vc_credit_counter_i (vc_credit_counter_toN ), + .vc_select_vld_o (vc_select_vld_toN ), + .vc_select_vc_id_o (vc_select_vc_id_toN ) +); + +output_port_vc_selection +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_S ), + .OUTPUT_VC_DEPTH (VC_DEPTH_OUTPUT_S ) +) +output_port_vc_selection_toS_u ( + .vc_credit_counter_i (vc_credit_counter_toS ), + .vc_select_vld_o (vc_select_vld_toS ), + .vc_select_vc_id_o (vc_select_vc_id_toS ) +); + +output_port_vc_selection +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_E ), + .OUTPUT_VC_DEPTH (VC_DEPTH_OUTPUT_E ) +) +output_port_vc_selection_toE_u ( + .vc_credit_counter_i (vc_credit_counter_toE ), + .vc_select_vld_o (vc_select_vld_toE ), + .vc_select_vc_id_o (vc_select_vc_id_toE ) +); + +output_port_vc_selection +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_W ), + .OUTPUT_VC_DEPTH (VC_DEPTH_OUTPUT_W ) +) +output_port_vc_selection_toW_u ( + .vc_credit_counter_i (vc_credit_counter_toW ), + .vc_select_vld_o (vc_select_vld_toW ), + .vc_select_vc_id_o (vc_select_vc_id_toW ) +); + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_output_port_vc_selection_toL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_output_port_vc_selection_toL + output_port_vc_selection + #( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_L ), + .OUTPUT_VC_DEPTH (VC_DEPTH_OUTPUT_L ), + + .OUTPUT_TO_L (1) + ) + output_port_vc_selection_toL_u ( + .vc_credit_counter_i (vc_credit_counter_toL [i] ), + .vc_select_vld_o (vc_select_vld_toL [i] ), + .vc_select_vc_id_o (vc_select_vc_id_toL [i] ) + ); + end + end +endgenerate + +// ============== +// vc assignment +// ============== + +io_port_t [SA_GLOBAL_INPUT_NUM_N-1:0] look_ahead_routing_all_inport_toN; +io_port_t [SA_GLOBAL_INPUT_NUM_S-1:0] look_ahead_routing_all_inport_toS; +io_port_t [SA_GLOBAL_INPUT_NUM_E-1:0] look_ahead_routing_all_inport_toE; +io_port_t [SA_GLOBAL_INPUT_NUM_W-1:0] look_ahead_routing_all_inport_toW; + + +assign look_ahead_routing_all_inport_toN[0] = look_ahead_routing[1]; +assign look_ahead_routing_all_inport_toN[1] = look_ahead_routing[2]; +assign look_ahead_routing_all_inport_toN[2] = look_ahead_routing[3]; +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_look_ahead_routing_all_inport_toN + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_look_ahead_routing_all_inport_toN + assign look_ahead_routing_all_inport_toN[3+i] = look_ahead_routing[4+i]; + end + end +endgenerate + +output_port_vc_assignment +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_N ), + .SA_GLOBAL_INPUT_NUM (SA_GLOBAL_INPUT_NUM_N ), + .OUTPUT_TO_N (1 ) +) +output_port_vc_assignment_toN_u ( + .sa_global_vld_i (sa_global_vld [0]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_i (sa_global_qos_value [0]), +`endif + .sa_global_inport_id_oh_i (sa_global_inport_id_oh [0][SA_GLOBAL_INPUT_NUM_N-1:0]), + .look_ahead_routing_i (look_ahead_routing_all_inport_toN ), + + .vc_select_vld_i (vc_select_vld_toN ), + .vc_select_vc_id_i (vc_select_vc_id_toN ), + + .vc_assignment_vld_o (vc_assignment_vld [0]), + .vc_assignment_vc_id_o (vc_assignment_vc_id [0]), + .look_ahead_routing_sel_o (look_ahead_routing_sel[0]) +); + +assign look_ahead_routing_all_inport_toS[0] = look_ahead_routing[0]; +assign look_ahead_routing_all_inport_toS[1] = look_ahead_routing[2]; +assign look_ahead_routing_all_inport_toS[2] = look_ahead_routing[3]; +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_look_ahead_routing_all_inport_toS + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_look_ahead_routing_all_inport_toS + assign look_ahead_routing_all_inport_toS[3+i] = look_ahead_routing[4+i]; + end + end +endgenerate + +output_port_vc_assignment +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_S ), + .SA_GLOBAL_INPUT_NUM (SA_GLOBAL_INPUT_NUM_S ), + .OUTPUT_TO_S (1 ) +) +output_port_vc_assignment_toS_u ( + .sa_global_vld_i (sa_global_vld [1]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_i (sa_global_qos_value [1]), +`endif + .sa_global_inport_id_oh_i (sa_global_inport_id_oh [1][SA_GLOBAL_INPUT_NUM_S-1:0]), + .look_ahead_routing_i (look_ahead_routing_all_inport_toS ), + + .vc_select_vld_i (vc_select_vld_toS ), + .vc_select_vc_id_i (vc_select_vc_id_toS ), + + .vc_assignment_vld_o (vc_assignment_vld [1]), + .vc_assignment_vc_id_o (vc_assignment_vc_id [1]), + .look_ahead_routing_sel_o (look_ahead_routing_sel[1]) +); + +assign look_ahead_routing_all_inport_toE[0] = look_ahead_routing[3]; +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_look_ahead_routing_all_inport_toE + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_look_ahead_routing_all_inport_toE + assign look_ahead_routing_all_inport_toE[1+i] = look_ahead_routing[4+i]; + end + end +endgenerate + +output_port_vc_assignment +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_E ), + .SA_GLOBAL_INPUT_NUM (SA_GLOBAL_INPUT_NUM_E ), + .OUTPUT_TO_E (1 ) +) +output_port_vc_assignment_toE_u ( + .sa_global_vld_i (sa_global_vld [2]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_i (sa_global_qos_value [2]), +`endif + .sa_global_inport_id_oh_i (sa_global_inport_id_oh [2][SA_GLOBAL_INPUT_NUM_E-1:0]), + .look_ahead_routing_i (look_ahead_routing_all_inport_toE), + + .vc_select_vld_i (vc_select_vld_toE ), + .vc_select_vc_id_i (vc_select_vc_id_toE ), + + .vc_assignment_vld_o (vc_assignment_vld [2]), + .vc_assignment_vc_id_o (vc_assignment_vc_id [2]), + .look_ahead_routing_sel_o (look_ahead_routing_sel[2]) +); + +assign look_ahead_routing_all_inport_toW[0] = look_ahead_routing[2]; +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_look_ahead_routing_all_inport_toW + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_look_ahead_routing_all_inport_toW + assign look_ahead_routing_all_inport_toW[1+i] = look_ahead_routing[4+i]; + end + end +endgenerate + +output_port_vc_assignment +#( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_W ), + .SA_GLOBAL_INPUT_NUM (SA_GLOBAL_INPUT_NUM_W ), + .OUTPUT_TO_W (1 ) +) +output_port_vc_assignment_toW_u ( + .sa_global_vld_i (sa_global_vld [3]), +`ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_i (sa_global_qos_value [3]), +`endif + .sa_global_inport_id_oh_i (sa_global_inport_id_oh [3][SA_GLOBAL_INPUT_NUM_W-1:0]), + .look_ahead_routing_i (look_ahead_routing_all_inport_toW), + + .vc_select_vld_i (vc_select_vld_toW ), + .vc_select_vc_id_i (vc_select_vc_id_toW ), + + .vc_assignment_vld_o (vc_assignment_vld [3]), + .vc_assignment_vc_id_o (vc_assignment_vc_id [3]), + .look_ahead_routing_sel_o (look_ahead_routing_sel[3]) +); + +generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_output_port_vc_assignment_toL + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_output_port_vc_assignment_toL + output_port_vc_assignment + #( + .OUTPUT_VC_NUM (VC_NUM_OUTPUT_L ), + .SA_GLOBAL_INPUT_NUM (SA_GLOBAL_INPUT_NUM_L ), + .OUTPUT_TO_L (1 ) + ) + output_port_vc_assignment_toL_u ( + .sa_global_vld_i (sa_global_vld [4+i]), + `ifdef COMMON_QOS_EXTRA_RT_VC + .sa_global_qos_value_i (sa_global_qos_value [4+i]), + `endif + .sa_global_inport_id_oh_i (sa_global_inport_id_oh [4+i][SA_GLOBAL_INPUT_NUM_L-1:0]), + .look_ahead_routing_i ({ {((SA_GLOBAL_INPUT_NUM_L-4)*$bits(io_port_t)){1'b0}}, // for flit to L port, the look ahead routing is meaningless + look_ahead_routing[3], + look_ahead_routing[2], + look_ahead_routing[1], + look_ahead_routing[0]} ), + + .vc_select_vld_i (vc_select_vld_toL [i] ), + .vc_select_vc_id_i (vc_select_vc_id_toL [i] ), + + .vc_assignment_vld_o (vc_assignment_vld [4+i]), + .vc_assignment_vc_id_o (vc_assignment_vc_id [4+i]), + .look_ahead_routing_sel_o (look_ahead_routing_sel[4+i]) + ); + end + end +endgenerate + +// ================== +// input buffer read // TODO: if change to sram as input buffer, the read should conduct right after local allocate +// ================== + +input_to_output +#( + .INPUT_PORT_NUM (INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM (OUTPUT_PORT_NUM ), + + .SA_GLOBAL_INPUT_NUM_N (SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S (SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E (SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W (SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L (SA_GLOBAL_INPUT_NUM_L ) +) +input_to_output_u +( + // input from sa global allocation + .sa_global_vld_i (sa_global_vld ), + // .sa_global_inport_id_i (sa_global_inport_id ), + .sa_global_inport_id_oh_i (sa_global_inport_id_oh ), + .sa_global_inport_vc_id_i (sa_global_inport_vc_id ), + + // input from vc allocation + .vc_assignment_vld_i (vc_assignment_vld ), + .vc_assignment_vc_id_i (vc_assignment_vc_id ), + .look_ahead_routing_sel_i (look_ahead_routing_sel ), + + // output to input port buffer to get selected flit + .inport_read_enable_o (inport_read_enable_sa_stage ), + // .inport_read_outport_id_o (inport_read_outport_id_sa_stage ), + .inport_read_vc_id_o (inport_read_vc_id_sa_stage ), + // .inport_look_ahead_routing_o (inport_look_ahead_routing_sa_stage ), + + // output to switch to let outport select inport + .outport_vld_o (outport_vld_sa_stage ), + .outport_select_inport_id_o (outport_select_inport_id_sa_stage ), + .outport_vc_id_o (outport_vc_id_sa_stage ), + .outport_look_ahead_routing_o (outport_look_ahead_routing_sa_stage ), + + // output to outport vc credit counter to consume one credit + .consume_vc_credit_vld_o (consume_vc_credit_vld ), + .consume_vc_credit_vc_id_o (consume_vc_credit_vc_id ) +); + +// =================== +// SA to ST stage reg +// =================== + +generate + for(i = 0; i < INPUT_PORT_NUM; i++) begin: gen_sa_to_st_reg_inport_angle + std_dffr + #(.WIDTH(1)) + U_STA_INPORT_READ_ENABLE_ST_STAGE + ( + .clk(clk), + .rstn(rstn), + .d(inport_read_enable_sa_stage[i]), + .q(inport_read_enable_st_stage[i]) + ); + + // std_dffe + // #(.WIDTH($bits(io_port_t))) + // U_DAT_INPORT_READ_OUTPORT_ID_ST_STAGE + // ( + // .clk(clk), + // .en(inport_read_enable_sa_stage[i]), + // .d(inport_read_outport_id_sa_stage[i]), + // .q(inport_read_outport_id_st_stage[i]) + // ); + + std_dffe + #(.WIDTH(VC_ID_NUM_MAX_W)) + U_DAT_INPORT_READ_VC_ID_ST_STAGE + ( + .clk(clk), + .en(inport_read_enable_sa_stage[i]), + .d(inport_read_vc_id_sa_stage[i]), + .q(inport_read_vc_id_st_stage[i]) + ); + + // std_dffe + // #(.WIDTH($bits(io_port_t))) + // U_DAT_INPORT_LOOK_AHEAD_ROUTING_ST_STAGE + // ( + // .clk(clk), + // .en(inport_read_enable_sa_stage[i]), + // .d(inport_look_ahead_routing_sa_stage[i]), + // .q(inport_look_ahead_routing_st_stage[i]) + // ); + end + + for(i = 0; i < OUTPUT_PORT_NUM; i++) begin: gen_sa_to_st_reg_outport_angle + std_dffr + #(.WIDTH(1)) + U_STA_OUTPORT_VLD_ST_STAGE + ( + .clk(clk), + .rstn(rstn), + .d(outport_vld_sa_stage[i]), + .q(outport_vld_st_stage[i]) + ); + + std_dffe + #(.WIDTH($bits(io_port_t))) + U_DAT_OUTPORT_SELECT_INPORT_ID_ST_STAGE + ( + .clk(clk), + .en(outport_vld_sa_stage[i]), + .d(outport_select_inport_id_sa_stage[i]), + .q(outport_select_inport_id_st_stage[i]) + ); + + std_dffe + #(.WIDTH(VC_ID_NUM_MAX_W)) + U_DAT_OUTPORT_VC_ID_ST_STAGE + ( + .clk(clk), + .en(outport_vld_sa_stage[i]), + .d(outport_vc_id_sa_stage[i]), + .q(outport_vc_id_st_stage[i]) + ); + + std_dffe + #(.WIDTH($bits(io_port_t))) + U_DAT_OUTPORT_LOOK_AHEAD_ROUTING_ST_STAGE + ( + .clk(clk), + .en(outport_vld_sa_stage[i]), + .d(outport_look_ahead_routing_sa_stage[i]), + .q(outport_look_ahead_routing_st_stage[i]) + ); + + end +endgenerate + + +// ================== +// switch to outport +// ================== + +switch +#( + .INPUT_PORT_NUM (INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM (OUTPUT_PORT_NUM ), + + .flit_payload_t (flit_payload_t ), + + .VC_NUM_INPUT_N (VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S (VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E (VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W (VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L (VC_NUM_INPUT_L ) + +) +switch_u +( + // input flit data from input port buffer + .vc_data_head_fromN_i (vc_data_head_N ), + .vc_data_head_fromS_i (vc_data_head_S ), + .vc_data_head_fromE_i (vc_data_head_E ), + .vc_data_head_fromW_i (vc_data_head_W ), +`ifdef HAVE_LOCAL_PORT + .vc_data_head_fromL_i (vc_data_head_L ), +`endif + + // input switch ctrl from SA to ST stage reg + .inport_read_enable_st_stage_i (inport_read_enable_st_stage), + .inport_read_vc_id_st_stage_i (inport_read_vc_id_st_stage ), + + .outport_vld_st_stage_i (outport_vld_st_stage ), + .outport_select_inport_id_st_stage_i (outport_select_inport_id_st_stage ), + .outport_vc_id_st_stage_i (outport_vc_id_st_stage ), + .outport_look_ahead_routing_st_stage_i(outport_look_ahead_routing_st_stage), + + // output flit data and look ahead routing to outport + .tx_flit_pend_o (tx_flit_pend_o ), + .tx_flit_v_o (tx_flit_v_o ), + .tx_flit_o (tx_flit_o ), + .tx_flit_vc_id_o (tx_flit_vc_id_o ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing_o ) + +); + +// =================== +// performance_monitor +// =================== +`ifndef SYNTHESIS +performance_monitor +#( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + + .VC_NUM_INPUT_N (VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S (VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E (VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W (VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L (VC_NUM_INPUT_L ), + + .VC_DEPTH_INPUT_N (VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S (VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E (VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W (VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L (VC_DEPTH_INPUT_L ) +) +v_performance_monitor_u ( + .sa_local_vld_i (sa_local_vld ), + .sa_global_inport_read_vld_i (inport_read_enable_sa_stage ), + + .vc_credit_counter_toN_i (vc_credit_counter_toN ), + .vc_credit_counter_toS_i (vc_credit_counter_toS ), + .vc_credit_counter_toE_i (vc_credit_counter_toE ), + .vc_credit_counter_toW_i (vc_credit_counter_toW ), + .vc_credit_counter_toL_i (vc_credit_counter_toL ), + + .node_id_x_ths_hop_i (node_id_x_ths_hop_i), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i), + .clk (clk ), + .rstn (rstn) +); +`endif + +endmodule diff --git a/tb/Makefile b/tb/Makefile new file mode 100644 index 0000000..6d6c54d --- /dev/null +++ b/tb/Makefile @@ -0,0 +1,40 @@ +CUR_MAKEFILE_PATH:=$(abspath $(lastword $(MAKEFILE_LIST))) +CUR_PATH:=$(patsubst %/,%, $(dir $(CUR_MAKEFILE_PATH))) +SRC_PATH:=$(CUR_PATH)/../rtl + +ELFIO_PATH:=$(CUR_PATH)/../../utils/elfio + +SIMULATOR_PATH:=$(CUR_PATH)/../../utils/dromajo +SIMULATOR_INC:=$(SIMULATOR_PATH)/include +SIMULATOR_BUILD_PATH:=$(SIMULATOR_PATH)/build + +RRVTB_PATH:=$(CUR_PATH)/../../utils/rrvtb + +default: mesh + +single_router: + vcs +vcs+lic+wait +vcs+loopreport -sverilog -kdb +vc -f flist_single_router.f $(DW_FILES) -top tb_single_router \ + +error+1 \ + +define+SIMULATION \ + +incdir+$(SRC_PATH) \ + +lint=TFIPC-L \ + -CFLAGS "-I$(SIMULATOR_INC) -I$(SIMULATOR_BUILD_PATH) -I$(ELFIO_PATH) -I$(RRVTB_PATH) -g -pthread -O3 -DGOLDMEM_INORDER" \ + -debug_access+all -full64 +vpi + +mesh: + vcs-2020.03-kgf vcs +vcs+lic+wait +vcs+loopreport -sverilog -kdb +vc -f flist_mesh.f $(DW_FILES) -top tb_mesh \ + +error+1 \ + +define+SIMULATION \ + +incdir+$(SRC_PATH) \ + +lint=TFIPC-L \ + -CFLAGS "-I$(SIMULATOR_INC) -I$(SIMULATOR_BUILD_PATH) -I$(ELFIO_PATH) -I$(RRVTB_PATH) -g -pthread -O3 -DGOLDMEM_INORDER" \ + -debug_access+all -full64 +vpi + +run: + ./simv +vcs+loopreport 2>&1 | tee run.log + +run_regression: + time ./simv +vcs+loopreport +dumpon=0 +self_finish=0 + +clean: + rm -rf simv* csrc Verdi* novas* ucli.key \ No newline at end of file diff --git a/tb/flist_mesh.f b/tb/flist_mesh.f new file mode 100644 index 0000000..1c7e060 --- /dev/null +++ b/tb/flist_mesh.f @@ -0,0 +1,59 @@ ++incdir+$PROJ_ROOT/rtl/include ++incdir+$PROJ_ROOT/tb + +$PROJ_ROOT/rtl/include/rvh_noc_pkg.sv +$PROJ_ROOT/tb/v_noc_pkg.sv + +$PROJ_ROOT/rtl/model/cells/std_dffe.sv +$PROJ_ROOT/rtl/model/cells/std_dffr.sv +$PROJ_ROOT/rtl/model/cells/std_dffre.sv +$PROJ_ROOT/rtl/model/cells/std_dffrve.sv + +$PROJ_ROOT/rtl/util/usage_manager.sv +$PROJ_ROOT/rtl/util/mp_fifo.sv +$PROJ_ROOT/rtl/util/mp_fifo_ptr_output.sv +$PROJ_ROOT/rtl/util/sp_fifo_dat_vld_output.sv +$PROJ_ROOT/rtl/util/one_counter.sv +$PROJ_ROOT/rtl/util/priority_encoder.sv +$PROJ_ROOT/rtl/util/onehot_mux.sv +$PROJ_ROOT/rtl/util/one_hot_priority_encoder.sv +$PROJ_ROOT/rtl/util/left_circular_rotate.sv +$PROJ_ROOT/rtl/util/oh2idx.sv +$PROJ_ROOT/rtl/util/one_hot_rr_arb.sv +$PROJ_ROOT/rtl/util/select_two_from_n_valid.sv +$PROJ_ROOT/rtl/util/freelist.sv + +$PROJ_ROOT/rtl/util/commoncell/src/Basic/hw/MuxOH.v +$PROJ_ROOT/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v + +// TODO: need to change to compiled dpsram +$PROJ_ROOT/rtl/model/simple_dual_one_clock.v + +$PROJ_ROOT/rtl/input_port.sv +$PROJ_ROOT/rtl/look_adead_routing.sv +$PROJ_ROOT/rtl/output_port_vc_selection.sv +$PROJ_ROOT/rtl/input_port_vc.sv +$PROJ_ROOT/rtl/output_port_vc_assignment.sv +$PROJ_ROOT/rtl/priority_req_select.sv +$PROJ_ROOT/rtl/sa_global.sv +$PROJ_ROOT/rtl/switch.sv +$PROJ_ROOT/rtl/input_port_flit_decoder.sv +$PROJ_ROOT/rtl/input_to_output.sv +$PROJ_ROOT/rtl/output_port_vc_credit_counter.sv +$PROJ_ROOT/rtl/sa_local.sv +$PROJ_ROOT/rtl/performance_monitor.sv +$PROJ_ROOT/rtl/vnet_router.sv + +$PROJ_ROOT/rtl/ruby/ut_lib.sv + +$PROJ_ROOT/rtl/local_port_look_adead_routing.sv +$PROJ_ROOT/rtl/local_port_couple_module.sv + +$PROJ_ROOT/tb/v_receiver.sv +$PROJ_ROOT/tb/v_scoreboard.sv +$PROJ_ROOT/tb/v_sender.sv +$PROJ_ROOT/tb/v_test_generator.sv + + +$PROJ_ROOT/tb/tb_mesh.sv +// $PROJ_ROOT/tb/testbench.sv diff --git a/tb/flist_mesh.syn.f b/tb/flist_mesh.syn.f new file mode 100644 index 0000000..234cc16 --- /dev/null +++ b/tb/flist_mesh.syn.f @@ -0,0 +1,47 @@ ++incdir+$PROJ_ROOT/rtl/include ++incdir+$PROJ_ROOT/tb + +$PROJ_ROOT/rtl/include/rvh_noc_pkg.sv + +$PROJ_ROOT/rtl/model/cells/std_dffe.sv +$PROJ_ROOT/rtl/model/cells/std_dffr.sv +$PROJ_ROOT/rtl/model/cells/std_dffre.sv +$PROJ_ROOT/rtl/model/cells/std_dffrve.sv + +$PROJ_ROOT/rtl/util/usage_manager.sv +$PROJ_ROOT/rtl/util/mp_fifo.sv +$PROJ_ROOT/rtl/util/mp_fifo_ptr_output.sv +$PROJ_ROOT/rtl/util/sp_fifo_dat_vld_output.sv +$PROJ_ROOT/rtl/util/one_counter.sv +$PROJ_ROOT/rtl/util/priority_encoder.sv +$PROJ_ROOT/rtl/util/onehot_mux.sv +$PROJ_ROOT/rtl/util/one_hot_priority_encoder.sv +$PROJ_ROOT/rtl/util/left_circular_rotate.sv +$PROJ_ROOT/rtl/util/oh2idx.sv +$PROJ_ROOT/rtl/util/one_hot_rr_arb.sv +$PROJ_ROOT/rtl/util/select_two_from_n_valid.sv +$PROJ_ROOT/rtl/util/freelist.sv + +$PROJ_ROOT/rtl/util/commoncell/src/Basic/hw/MuxOH.v +$PROJ_ROOT/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v + +// TODO: need to change to compiled dpsram +$PROJ_ROOT/rtl/rtl/model/simple_dual_one_clock.v + +$PROJ_ROOT/rtl/input_port.sv +$PROJ_ROOT/rtl/look_adead_routing.sv +$PROJ_ROOT/rtl/output_port_vc_selection.sv +$PROJ_ROOT/rtl/input_port_vc.sv +$PROJ_ROOT/rtl/output_port_vc_assignment.sv +$PROJ_ROOT/rtl/priority_req_select.sv +$PROJ_ROOT/rtl/sa_global.sv +$PROJ_ROOT/rtl/switch.sv +$PROJ_ROOT/rtl/input_port_flit_decoder.sv +$PROJ_ROOT/rtl/input_to_output.sv +$PROJ_ROOT/rtl/output_port_vc_credit_counter.sv +$PROJ_ROOT/rtl/sa_local.sv +$PROJ_ROOT/rtl/performance_monitor.sv +$PROJ_ROOT/rtl/vnet_router.sv + +$PROJ_ROOT/tb/top_mesh_syn.sv +// $PROJ_ROOT/tb/testbench.sv diff --git a/tb/flist_single_router.f b/tb/flist_single_router.f new file mode 100644 index 0000000..2eb9152 --- /dev/null +++ b/tb/flist_single_router.f @@ -0,0 +1,59 @@ ++incdir+$PROJ_ROOT/rtl/include ++incdir+$PROJ_ROOT/tb + +$PROJ_ROOT/rtl/include/rvh_noc_pkg.sv + +$PROJ_ROOT/rtl/model/cells/std_dffe.sv +$PROJ_ROOT/rtl/model/cells/std_dffr.sv +$PROJ_ROOT/rtl/model/cells/std_dffre.sv +$PROJ_ROOT/rtl/model/cells/std_dffrve.sv + +$PROJ_ROOT/rtl/util/usage_manager.sv +$PROJ_ROOT/rtl/util/mp_fifo.sv +$PROJ_ROOT/rtl/util/mp_fifo_ptr_output.sv +$PROJ_ROOT/rtl/util/sp_fifo_dat_vld_output.sv +$PROJ_ROOT/rtl/util/one_counter.sv +$PROJ_ROOT/rtl/util/priority_encoder.sv +$PROJ_ROOT/rtl/util/onehot_mux.sv +$PROJ_ROOT/rtl/util/one_hot_priority_encoder.sv +$PROJ_ROOT/rtl/util/left_circular_rotate.sv +$PROJ_ROOT/rtl/util/oh2idx.sv +$PROJ_ROOT/rtl/util/one_hot_rr_arb.sv +$PROJ_ROOT/rtl/util/select_two_from_n_valid.sv +$PROJ_ROOT/rtl/util/freelist.sv + +$PROJ_ROOT/rtl/util/commoncell/src/Basic/hw/MuxOH.v +$PROJ_ROOT/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v + +// TODO: need to change to compiled dpsram +$PROJ_ROOT/rtl/rtl/model/simple_dual_one_clock.v + +$PROJ_ROOT/rtl/input_port.sv +$PROJ_ROOT/rtl/look_adead_routing.sv +$PROJ_ROOT/rtl/output_port_vc_selection.sv +$PROJ_ROOT/rtl/input_port_vc.sv +$PROJ_ROOT/rtl/output_port_vc_assignment.sv +$PROJ_ROOT/rtl/priority_req_select.sv +$PROJ_ROOT/rtl/sa_global.sv +$PROJ_ROOT/rtl/switch.sv +$PROJ_ROOT/rtl/input_port_flit_decoder.sv +$PROJ_ROOT/rtl/input_to_output.sv +$PROJ_ROOT/rtl/output_port_vc_credit_counter.sv +$PROJ_ROOT/rtl/sa_local.sv +$PROJ_ROOT/rtl/performance_monitor.sv +$PROJ_ROOT/rtl/vnet_router.sv + +$PROJ_ROOT/tb/v_noc_pkg.sv +$PROJ_ROOT/rtl/rvh_l1d/ruby/ut_lib.sv + +$PROJ_ROOT/rtl/local_port_look_adead_routing.sv +$PROJ_ROOT/rtl/local_port_couple_module.sv + +$PROJ_ROOT/tb/v_receiver.sv +$PROJ_ROOT/tb/v_scoreboard.sv +$PROJ_ROOT/tb/v_sender.sv +$PROJ_ROOT/tb/v_test_generator.sv + + +$PROJ_ROOT/tb/tb_single_router.sv +// $PROJ_ROOT/tb/testbench.sv diff --git a/tb/flist_single_router.syn.f b/tb/flist_single_router.syn.f new file mode 100644 index 0000000..17b3914 --- /dev/null +++ b/tb/flist_single_router.syn.f @@ -0,0 +1,47 @@ ++incdir+$PROJ_ROOT/rtl/include ++incdir+$PROJ_ROOT/tb + +$PROJ_ROOT/rtl/include/rvh_noc_pkg.sv + +$PROJ_ROOT/rtl/model/cells/std_dffe.sv +$PROJ_ROOT/rtl/model/cells/std_dffr.sv +$PROJ_ROOT/rtl/model/cells/std_dffre.sv +$PROJ_ROOT/rtl/model/cells/std_dffrve.sv + +$PROJ_ROOT/rtl/util/usage_manager.sv +$PROJ_ROOT/rtl/util/mp_fifo.sv +$PROJ_ROOT/rtl/util/mp_fifo_ptr_output.sv +$PROJ_ROOT/rtl/util/sp_fifo_dat_vld_output.sv +$PROJ_ROOT/rtl/util/one_counter.sv +$PROJ_ROOT/rtl/util/priority_encoder.sv +$PROJ_ROOT/rtl/util/onehot_mux.sv +$PROJ_ROOT/rtl/util/one_hot_priority_encoder.sv +$PROJ_ROOT/rtl/util/left_circular_rotate.sv +$PROJ_ROOT/rtl/util/oh2idx.sv +$PROJ_ROOT/rtl/util/one_hot_rr_arb.sv +$PROJ_ROOT/rtl/util/select_two_from_n_valid.sv +$PROJ_ROOT/rtl/util/freelist.sv + +$PROJ_ROOT/rtl/util/commoncell/src/Basic/hw/MuxOH.v +$PROJ_ROOT/rtl/util/commoncell/src/Queue/hw/AgeMatrixSelector.v + +// TODO: need to change to compiled dpsram +$PROJ_ROOT/rtl/rtl/model/simple_dual_one_clock.v + +$PROJ_ROOT/rtl/input_port.sv +$PROJ_ROOT/rtl/look_adead_routing.sv +$PROJ_ROOT/rtl/output_port_vc_selection.sv +$PROJ_ROOT/rtl/input_port_vc.sv +$PROJ_ROOT/rtl/output_port_vc_assignment.sv +$PROJ_ROOT/rtl/priority_req_select.sv +$PROJ_ROOT/rtl/sa_global.sv +$PROJ_ROOT/rtl/switch.sv +$PROJ_ROOT/rtl/input_port_flit_decoder.sv +$PROJ_ROOT/rtl/input_to_output.sv +$PROJ_ROOT/rtl/output_port_vc_credit_counter.sv +$PROJ_ROOT/rtl/sa_local.sv +$PROJ_ROOT/rtl/performance_monitor.sv +$PROJ_ROOT/rtl/vnet_router.sv + +$PROJ_ROOT/tb/top_single_router_syn.sv +// $PROJ_ROOT/tb/testbench.sv diff --git a/tb/tb_mesh.sv b/tb/tb_mesh.sv new file mode 100644 index 0000000..077e30d --- /dev/null +++ b/tb/tb_mesh.sv @@ -0,0 +1,567 @@ +module tb_mesh +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + // mesh parameters + parameter NODE_NUM_X_DIMESION = 3, + parameter NODE_NUM_Y_DIMESION = 3, + + // router parameters + parameter INPUT_PORT_NUM = INPUT_PORT_NUMBER, + parameter OUTPUT_PORT_NUM = OUTPUT_PORT_NUMBER, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + parameter type flit_payload_t = logic[FLIT_LENGTH-1:0], + + // parameter QOS_VC_NUM_PER_INPUT = QOS_VC_NUM_PER_INPUT, + + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter VC_NUM_INPUT_L = 4+LOCAL_PORT_NUM-1+QOS_VC_NUM_PER_INPUT, +`else + parameter VC_NUM_INPUT_L = 4+QOS_VC_NUM_PER_INPUT, +`endif + parameter SA_GLOBAL_INPUT_NUM_N = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_S = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_E = 1+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_W = 1+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter SA_GLOBAL_INPUT_NUM_L = 4+LOCAL_PORT_NUM-1, +`else + parameter SA_GLOBAL_INPUT_NUM_L = 4, +`endif + parameter VC_NUM_OUTPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_L = 1, + parameter VC_DEPTH_INPUT_N = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_S = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_E = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_W = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_L = VC_DEPTH_MAX, + + parameter int V_CPU_DATA_REQ_NUM_PER_CORE_PER_CYCLE = 3, + parameter int V_CPU_INST_REQ_NUM_PER_CORE_PER_CYCLE = 1, + parameter real V_L1D_MISS_RATE = 10, // % + parameter real V_L1I_MISS_RATE = 10, // % + parameter real V_L2_MISS_RATE = 100, // % + parameter int V_CORE_NUM = NODE_NUM_X_DIMESION * NODE_NUM_Y_DIMESION * LOCAL_PORT_NUM, + parameter int V_CACHE_MISS_ALL_CORE_PER_CYCLE = V_CORE_NUM, + // parameter int V_CACHE_MISS_ALL_CORE_PER_CYCLE = ((V_CPU_DATA_REQ_NUM_PER_CORE_PER_CYCLE * V_CORE_NUM) * V_L1D_MISS_RATE + + // (V_CPU_INST_REQ_NUM_PER_CORE_PER_CYCLE * V_CORE_NUM) * V_L1I_MISS_RATE) * + // V_L2_MISS_RATE / 100 / 100, + + + // test_generator parameters + parameter TEST_CASE_MESH_RANDOM = 1, // random sender and receiver + parameter TEST_CASE_MESH_DIAGONAL = !TEST_CASE_MESH_RANDOM, // from (0,0) to (NODE_NUM_X_DIMESION-1, NODE_NUM_Y_DIMESION-1) + + parameter RANDOM_BIT_NUM = 168, // 32,64,80,128,168 + + parameter SCOREBOARD_TIMEOUT_EN = !TEST_CASE_MESH_DIAGONAL, + parameter SCOREBOARD_TIMEOUT_THRESHOLD = 16384, + + parameter TEST_CASE_NUM_PER_CYCLE = RANDOM_BIT_NUM/3, + // parameter TEST_CASE_NUM_PER_CYCLE = V_CACHE_MISS_ALL_CORE_PER_CYCLE < 1 ? 1 : + // V_CACHE_MISS_ALL_CORE_PER_CYCLE > RANDOM_BIT_NUM/3 ? RANDOM_BIT_NUM/3 : // no more than RANDOM_BIT_NUM/3 + // V_CACHE_MISS_ALL_CORE_PER_CYCLE, + + // scoreboard parameters + parameter SCOREBOARD_ENTRY_NUM_PER_SENDER = 64, + + // sender parameters + parameter SENDER_NUM = NODE_NUM_X_DIMESION*NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM, + parameter SENDER_FLIT_BUFFER_DEPTH = 512, + + parameter SENDER_TIMEOUT_EN = !TEST_CASE_MESH_DIAGONAL, + parameter SENDER_TIMEOUT_THRESHOLD = 16384, + + // receiver parameters + parameter RECEIVER_NUM = SENDER_NUM, + + // overall longest test cycle + parameter LONGEST_TEST_CYCLE = 100000 +) +( + +); + + genvar i, j, k; + + // Ports + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_pend; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_v; + flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id; + io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_pend; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_v; + flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id; + io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0] tx_lcrd_v; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0] rx_lcrd_v; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id; + + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][NodeID_X_Width-1:0] node_id_x; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][NodeID_Y_Width-1:0] node_id_y; + + logic clk; + logic rstn; + + + // generate mesh routers + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_mesh_routers_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_mesh_routers_y_dimesion + vnet_router + #( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + .flit_payload_t(flit_payload_t), + .QOS_VC_NUM_PER_INPUT(QOS_VC_NUM_PER_INPUT), + .VC_NUM_INPUT_N(VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S(VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E(VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W(VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L(VC_NUM_INPUT_L ), + .SA_GLOBAL_INPUT_NUM_N(SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S(SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E(SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W(SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L(SA_GLOBAL_INPUT_NUM_L ), + .VC_NUM_OUTPUT_N(VC_NUM_OUTPUT_N ), + .VC_NUM_OUTPUT_S(VC_NUM_OUTPUT_S ), + .VC_NUM_OUTPUT_E(VC_NUM_OUTPUT_E ), + .VC_NUM_OUTPUT_W(VC_NUM_OUTPUT_W ), + .VC_NUM_OUTPUT_L(VC_NUM_OUTPUT_L ), + .VC_DEPTH_INPUT_N(VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S(VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E(VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W(VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L(VC_DEPTH_INPUT_L ) + ) + vnet_router_dut ( + .rx_flit_pend_i (rx_flit_pend [i][j] ), + .rx_flit_v_i (rx_flit_v [i][j] ), + .rx_flit_i (rx_flit [i][j] ), + .rx_flit_vc_id_i (rx_flit_vc_id [i][j] ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing [i][j] ), + + .tx_flit_pend_o (tx_flit_pend [i][j] ), + .tx_flit_v_o (tx_flit_v [i][j] ), + .tx_flit_o (tx_flit [i][j] ), + .tx_flit_vc_id_o (tx_flit_vc_id [i][j] ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing [i][j] ), + + .rx_lcrd_v_o (rx_lcrd_v [i][j] ), + .rx_lcrd_id_o (rx_lcrd_id [i][j] ), + + .tx_lcrd_v_i (tx_lcrd_v [i][j] ), + .tx_lcrd_id_i (tx_lcrd_id [i][j] ), + + .node_id_x_ths_hop_i (node_id_x [i][j] ), + .node_id_y_ths_hop_i (node_id_y [i][j] ), + + .clk (clk ), + .rstn (rstn) + ); + end + end + endgenerate + + // assign node id to each router + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_node_id_x_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_node_id_x_y_dimesion + assign node_id_x [i][j] = i; + end + end + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_node_id_y_y_dimesion + for(j = 0; j < NODE_NUM_X_DIMESION; j++) begin: gen_node_id_y_x_dimesion + assign node_id_y [j][i] = i; + end + end + endgenerate + + // connect each router together + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_connect_routers_ns_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION-1; j++) begin: gen_connect_routers_ns_y_dimesion + // connect N inport to S outport + assign rx_flit_pend [i][j][0] = tx_flit_pend [i][j+1][1]; + assign rx_flit_v [i][j][0] = tx_flit_v [i][j+1][1]; + assign rx_flit [i][j][0] = tx_flit [i][j+1][1]; + assign rx_flit_vc_id [i][j][0] = tx_flit_vc_id [i][j+1][1]; + assign rx_flit_look_ahead_routing [i][j][0] = tx_flit_look_ahead_routing [i][j+1][1]; + + assign tx_lcrd_v [i][j][0] = rx_lcrd_v [i][j+1][1]; + assign tx_lcrd_id [i][j][0] = rx_lcrd_id [i][j+1][1]; + + // connect S inport to N outport + assign rx_flit_pend [i][j+1][1] = tx_flit_pend [i][j][0]; + assign rx_flit_v [i][j+1][1] = tx_flit_v [i][j][0]; + assign rx_flit [i][j+1][1] = tx_flit [i][j][0]; + assign rx_flit_vc_id [i][j+1][1] = tx_flit_vc_id [i][j][0]; + assign rx_flit_look_ahead_routing [i][j+1][1] = tx_flit_look_ahead_routing [i][j][0]; + + assign tx_lcrd_v [i][j+1][1] = rx_lcrd_v [i][j][0]; + assign tx_lcrd_id [i][j+1][1] = rx_lcrd_id [i][j][0]; + end + end + endgenerate + + generate + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_connect_routers_ew_x_dimesion + for(j = 0; j < NODE_NUM_X_DIMESION-1; j++) begin: gen_connect_routers_ew_y_dimesion + // connect E inport to W outport + assign rx_flit_pend [j][i][2] = tx_flit_pend [j+1][i][3]; + assign rx_flit_v [j][i][2] = tx_flit_v [j+1][i][3]; + assign rx_flit [j][i][2] = tx_flit [j+1][i][3]; + assign rx_flit_vc_id [j][i][2] = tx_flit_vc_id [j+1][i][3]; + assign rx_flit_look_ahead_routing [j][i][2] = tx_flit_look_ahead_routing [j+1][i][3]; + + assign tx_lcrd_v [j][i][2] = rx_lcrd_v [j+1][i][3]; + assign tx_lcrd_id [j][i][2] = rx_lcrd_id [j+1][i][3]; + + // connect W inport to E outport + assign rx_flit_pend [j+1][i][3] = tx_flit_pend [j][i][2]; + assign rx_flit_v [j+1][i][3] = tx_flit_v [j][i][2]; + assign rx_flit [j+1][i][3] = tx_flit [j][i][2]; + assign rx_flit_vc_id [j+1][i][3] = tx_flit_vc_id [j][i][2]; + assign rx_flit_look_ahead_routing [j+1][i][3] = tx_flit_look_ahead_routing [j][i][2]; + + assign tx_lcrd_v [j+1][i][3] = rx_lcrd_v [j][i][2]; + assign tx_lcrd_id [j+1][i][3] = rx_lcrd_id [j][i][2]; + end + end + endgenerate + + // other unused non-local ports, assign router rx to 0 + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_unused_non_local_ports_x_dimesion + assign rx_flit_pend [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_v [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_vc_id [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_look_ahead_routing [i][NODE_NUM_Y_DIMESION-1][0] = '0; + + assign tx_lcrd_v [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign tx_lcrd_id [i][NODE_NUM_Y_DIMESION-1][0] = '0; + + + assign rx_flit_pend [i][0][1] = '0; + assign rx_flit_v [i][0][1] = '0; + assign rx_flit [i][0][1] = '0; + assign rx_flit_vc_id [i][0][1] = '0; + assign rx_flit_look_ahead_routing [i][0][1] = '0; + + assign tx_lcrd_v [i][0][1] = '0; + assign tx_lcrd_id [i][0][1] = '0; + end + + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_unused_non_local_ports_y_dimesion + // connect E inport to W outport + assign rx_flit_pend [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_v [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_vc_id [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_look_ahead_routing [NODE_NUM_X_DIMESION-1][i][2] = '0; + + assign tx_lcrd_v [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign tx_lcrd_id [NODE_NUM_X_DIMESION-1][i][2] = '0; + + // connect W inport to E outport + assign rx_flit_pend [0][i][3] = '0; + assign rx_flit_v [0][i][3] = '0; + assign rx_flit [0][i][3] = '0; + assign rx_flit_vc_id [0][i][3] = '0; + assign rx_flit_look_ahead_routing [0][i][3] = '0; + + assign tx_lcrd_v [0][i][3] = '0; + assign tx_lcrd_id [0][i][3] = '0; + end + endgenerate + + + // test generate + logic [64-1:0] mcycle; + node_id_t target_node; + + int longest_test_cycle = LONGEST_TEST_CYCLE; + int self_finish = 1; + + initial begin + $value$plusargs("longest_test_cycle=%d", longest_test_cycle); + $value$plusargs("self_finish=%d", self_finish); + end + + always_ff @(posedge clk or negedge rstn) begin + if(~rstn) begin + mcycle <= '0; + end else begin + mcycle <= mcycle + 1; + if(self_finish > 0) begin + if(mcycle == longest_test_cycle) begin + $finish(); + end + end + end + end + + logic [32-1:0] src_id_lfsr_seed; + logic [32-1:0] tgt_id_lfsr_seed; + logic [SENDER_NUM-1:0] new_test_vld; + test_case_t [SENDER_NUM-1:0] new_test; + logic [SENDER_NUM-1:0] new_test_rdy; + + assign src_id_lfsr_seed = 32'hdeadbeef; + assign tgt_id_lfsr_seed = 32'hbaadf00d; + + v_test_generator + #( + .SENDER_NUM (SENDER_NUM ), + .RANDOM_BIT_NUM (RANDOM_BIT_NUM ), + .SCOREBOARD_TIMEOUT_EN (SCOREBOARD_TIMEOUT_EN ), + .SCOREBOARD_TIMEOUT_THRESHOLD (SCOREBOARD_TIMEOUT_THRESHOLD ), + .TEST_CASE_NUM_PER_CYCLE(TEST_CASE_NUM_PER_CYCLE ), + .TEST_CASE_MESH_RANDOM (TEST_CASE_MESH_RANDOM ), + .TEST_CASE_MESH_DIAGONAL(TEST_CASE_MESH_DIAGONAL ), + .NODE_NUM_X_DIMESION (NODE_NUM_X_DIMESION ), + .NODE_NUM_Y_DIMESION (NODE_NUM_Y_DIMESION ), + .LOCAL_PORT_NUM (LOCAL_PORT_NUM ), + + .ASSUMED_SYSTEM_FREQUENCY((1<<30) ) + ) + v_test_generator_u ( + .new_test_vld_o (new_test_vld ), + .new_test_o (new_test ), + .new_test_rdy_i (new_test_rdy ), + .src_id_lfsr_seed_i (src_id_lfsr_seed ^ mcycle[16+:32]), + .tgt_id_lfsr_seed_i (tgt_id_lfsr_seed ^ mcycle[20+:32] ), + .lfsr_update_en_i (&mcycle[16-1:0]), + .mcycle_i (mcycle), + .clk (clk ), + .rstn (rstn) + ); + + logic [SENDER_NUM-1:0] new_scoreboard_entry_vld; + scoreboard_entry_t [SENDER_NUM-1:0] new_scoreboard_entry; + logic [SENDER_NUM-1:0] new_scoreboard_entry_rdy; + node_id_t [SENDER_NUM-1:0] sender_node_id; + + +// local port map to sender number +// sender id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id +// 7 8 17 16 +// \ | | / +// 6 - (0,2) -- (1,2) - 15 +// 5 | | 14 +// \ | | / +// 4 - (0,1) -- (1,1) - 13 +// / | | \ +// 3 | | 12 +// 2 - (0,0) -- (1,0) - 11 +// / | | \ +// 1 0 9 10 + + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_sender_node_id_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_sender_node_id_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_sender_node_id_device_port + assign sender_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].x_position = i; + assign sender_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].y_position = j; + assign sender_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].device_port = k; + assign sender_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].device_id = '0; + end + end + end + endgenerate + + +generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_v_sender_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_v_sender_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_v_sender_device_port + v_sender + #( + .FLIT_BUFFER_DEPTH (SENDER_FLIT_BUFFER_DEPTH), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_L), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_L ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_L (1) + ) + v_sender_toL_u ( + .tx_flit_pend_o (rx_flit_pend [i][j][4+k] ), + .tx_flit_v_o (rx_flit_v [i][j][4+k] ), + .tx_flit_o (rx_flit [i][j][4+k] ), + .tx_flit_vc_id_o (rx_flit_vc_id [i][j][4+k] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing [i][j][4+k] ), + + .tx_lcrd_v_i (rx_lcrd_v [i][j][4+k] ), + .tx_lcrd_id_i (rx_lcrd_id [i][j][4+k] ), + + // sender id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id + .new_test_vld_i (new_test_vld [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .new_test_i (new_test [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .new_test_rdy_o (new_test_rdy [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .new_scoreboard_entry_o (new_scoreboard_entry [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + + .node_id_i (sender_node_id [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + end + end + end +endgenerate + + + logic [RECEIVER_NUM-1:0] check_scoreboard_vld; + receiver_info_t [RECEIVER_NUM-1:0] check_scoreboard; + logic [RECEIVER_NUM-1:0] check_scoreboard_rdy; + node_id_t [RECEIVER_NUM-1:0] receiver_node_id; + +// local port map to receiver number +// receiver id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id +// 7 8 17 16 +// \ | | / +// 6 - (0,2) -- (1,2) - 15 +// 5 | | 14 +// \ | | / +// 4 - (0,1) -- (1,1) - 13 +// / | | \ +// 3 | | 12 +// 2 - (0,0) -- (1,0) - 11 +// / | | \ +// 1 0 9 10 + + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_receiver_node_id_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_receiver_node_id_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_receiver_node_id_device_port + assign receiver_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].x_position = i; + assign receiver_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].y_position = j; + assign receiver_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].device_port = k; + assign receiver_node_id[i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k].device_id = '0; + end + end + end + endgenerate + + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_v_receiver_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_v_receiver_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_v_sender_device_port + v_receiver + #( + .flit_payload_t (flit_payload_t ) + ) + v_receiver_u ( + .rx_flit_pend_i (tx_flit_pend [i][j][4+k] ), + .rx_flit_v_i (tx_flit_v [i][j][4+k] ), + .rx_flit_i (tx_flit [i][j][4+k] ), + .rx_flit_vc_id_i (tx_flit_vc_id [i][j][4+k] ), + .rx_flit_look_ahead_routing_i (tx_flit_look_ahead_routing [i][j][4+k] ), + + .rx_lcrd_v_o (tx_lcrd_v [i][j][4+k] ), + .rx_lcrd_id_o (tx_lcrd_id [i][j][4+k] ), + + // receiver id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id + .check_scoreboard_vld_o (check_scoreboard_vld [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .check_scoreboard_o (check_scoreboard [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + .check_scoreboard_rdy_i (check_scoreboard_rdy [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + + .node_id_i (receiver_node_id [i*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + j*LOCAL_PORT_NUM + k] ), + + .clk (clk ), + .rstn (rstn) + ); + end + end + end + endgenerate + + + v_scoreboard + #( + .SCOREBOARD_ENTRY_NUM_PER_SENDER (SCOREBOARD_ENTRY_NUM_PER_SENDER ), + .SENDER_NUM (SENDER_NUM ), + .RECEIVER_NUM (RECEIVER_NUM ), + + .NODE_NUM_X_DIMESION (NODE_NUM_X_DIMESION ), + .NODE_NUM_Y_DIMESION (NODE_NUM_Y_DIMESION ), + .LOCAL_PORT_NUM (LOCAL_PORT_NUM ), + + .ASSUMED_SYSTEM_FREQUENCY ((1<<30) ) + ) + v_scoreboard_u ( + .new_scoreboard_entry_vld_i (new_scoreboard_entry_vld ), + .new_scoreboard_entry_i (new_scoreboard_entry ), + .new_scoreboard_entry_rdy_o (new_scoreboard_entry_rdy ), + + .check_scoreboard_vld_i (check_scoreboard_vld ), + .check_scoreboard_i (check_scoreboard ), + .check_scoreboard_rdy_o (check_scoreboard_rdy ), + + .mcycle_i (mcycle ), + + .clk (clk ), + .rstn (rstn) + ); + + + + + + //clock generate + initial begin + clk = 1'b0; + forever #5 clk = ~clk; + end + + //reset generate + initial begin + rstn = 1'b0; + #30; + rstn = 1'b1; + end + + initial begin + int dumpon = 1; + int vcdplus = 0; + $value$plusargs("dumpon=%d", dumpon); + $value$plusargs("vcdplus=%d", vcdplus); + + if (dumpon > 0) begin + // $fsdbDumpvars(0, tb_mesh); + // $fsdbDumpvars("+struct"); + // $fsdbDumpvars("+mda"); + // $fsdbDumpvars("+all"); + // $fsdbDumpon; + $vcdpluson(); + end + if (vcdplus > 0) begin + $vcdpluson(); + end + end + +endmodule diff --git a/tb/tb_single_router.sv b/tb/tb_single_router.sv new file mode 100644 index 0000000..2737950 --- /dev/null +++ b/tb/tb_single_router.sv @@ -0,0 +1,559 @@ +module tb_single_router +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + // router parameters + parameter INPUT_PORT_NUM = INPUT_PORT_NUMBER, + parameter OUTPUT_PORT_NUM = OUTPUT_PORT_NUMBER, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + parameter type flit_payload_t = logic[FLIT_LENGTH-1:0], + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter VC_NUM_INPUT_L = 4+LOCAL_PORT_NUM-1, +`else + parameter VC_NUM_INPUT_L = 4, +`endif + parameter SA_GLOBAL_INPUT_NUM_N = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_S = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_E = 1+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_W = 1+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter SA_GLOBAL_INPUT_NUM_L = 4+LOCAL_PORT_NUM-1, +`else + parameter SA_GLOBAL_INPUT_NUM_L = 4, +`endif + parameter VC_NUM_OUTPUT_N = 1+LOCAL_PORT_NUM, + parameter VC_NUM_OUTPUT_S = 1+LOCAL_PORT_NUM, + parameter VC_NUM_OUTPUT_E = 3+LOCAL_PORT_NUM, + parameter VC_NUM_OUTPUT_W = 3+LOCAL_PORT_NUM, + parameter VC_NUM_OUTPUT_L = 1, + parameter VC_DEPTH_INPUT_N = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_S = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_E = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_W = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_L = VC_DEPTH_MAX, + + // test_generator parameters + parameter RANDOM_BIT_NUM = 32, + + parameter SCOREBOARD_TIMEOUT_EN = 1, + parameter SCOREBOARD_TIMEOUT_THRESHOLD = 256, + + parameter TEST_CASE_NUM_PER_CYCLE = 10, + + // scoreboard parameters + parameter SCOREBOARD_ENTRY_NUM_PER_SENDER = 64, + + // sender parameters + parameter SENDER_TIMEOUT_EN = 1, + parameter SENDER_TIMEOUT_THRESHOLD = 256, + + // overall longest test cycle + parameter LONGEST_TEST_CYCLE = 10000 +) +( + +); + + genvar i; + + // Ports + logic [INPUT_PORT_NUM-1:0] rx_flit_pend_i; + logic [INPUT_PORT_NUM-1:0] rx_flit_v_i; + flit_payload_t [INPUT_PORT_NUM-1:0] rx_flit_i; + logic [INPUT_PORT_NUM-1:0] [VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id_i; + io_port_t [INPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing_i; + + logic [OUTPUT_PORT_NUM-1:0] tx_flit_pend_o; + logic [OUTPUT_PORT_NUM-1:0] tx_flit_v_o; + flit_payload_t [OUTPUT_PORT_NUM-1:0] tx_flit_o; + logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o; + io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing_o; + + logic [INPUT_PORT_NUM-1:0] rx_lcrd_v_o; + logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o; + + logic [OUTPUT_PORT_NUM-1:0] tx_lcrd_v_i; + logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i; + + logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i; + logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i; + + logic clk; + logic rstn; + + assign node_id_x_ths_hop_i = 2'b01; + assign node_id_y_ths_hop_i = 2'b01; + + vnet_router + #( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + .flit_payload_t(flit_payload_t), + .VC_NUM_INPUT_N(VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S(VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E(VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W(VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L(VC_NUM_INPUT_L ), + .SA_GLOBAL_INPUT_NUM_N(SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S(SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E(SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W(SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L(SA_GLOBAL_INPUT_NUM_L ), + .VC_NUM_OUTPUT_N(VC_NUM_OUTPUT_N ), + .VC_NUM_OUTPUT_S(VC_NUM_OUTPUT_S ), + .VC_NUM_OUTPUT_E(VC_NUM_OUTPUT_E ), + .VC_NUM_OUTPUT_W(VC_NUM_OUTPUT_W ), + .VC_NUM_OUTPUT_L(VC_NUM_OUTPUT_L ), + .VC_DEPTH_INPUT_N(VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S(VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E(VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W(VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L(VC_DEPTH_INPUT_L ) + ) + vnet_router_dut ( + .rx_flit_pend_i (rx_flit_pend_i ), + .rx_flit_v_i (rx_flit_v_i ), + .rx_flit_i (rx_flit_i ), + .rx_flit_vc_id_i (rx_flit_vc_id_i ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i ), + .tx_flit_pend_o (tx_flit_pend_o ), + .tx_flit_v_o (tx_flit_v_o ), + .tx_flit_o (tx_flit_o ), + .tx_flit_vc_id_o (tx_flit_vc_id_o ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing_o ), + .rx_lcrd_v_o (rx_lcrd_v_o ), + .rx_lcrd_id_o (rx_lcrd_id_o ), + .tx_lcrd_v_i (tx_lcrd_v_i ), + .tx_lcrd_id_i (tx_lcrd_id_i ), + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .clk (clk ), + .rstn (rstn) + ); + + // test generate + logic [64-1:0] mcycle; + node_id_t target_node; + + int longest_test_cycle = LONGEST_TEST_CYCLE; + int self_finish = 1; + + initial begin + $value$plusargs("longest_test_cycle=%d", longest_test_cycle); + $value$plusargs("self_finish=%d", self_finish); + end + + always_ff @(posedge clk or negedge rstn) begin + if(~rstn) begin + mcycle <= '0; + end else begin + mcycle <= mcycle + 1; + if(self_finish > 0) begin + if(mcycle == longest_test_cycle) begin + $finish(); + end + end + end + end + + logic [32-1:0] src_id_lfsr_seed; + logic [32-1:0] tgt_id_lfsr_seed; + logic [INPUT_PORT_NUM-1:0] new_test_vld; + test_case_t [INPUT_PORT_NUM-1:0] new_test; + logic [INPUT_PORT_NUM-1:0] new_test_rdy; + + assign src_id_lfsr_seed = 32'hdeadbeef; + assign tgt_id_lfsr_seed = 32'hbaadf00d; + + v_test_generator + #( + .SENDER_NUM (INPUT_PORT_NUM ), + .RANDOM_BIT_NUM (RANDOM_BIT_NUM ), + .SCOREBOARD_TIMEOUT_EN (SCOREBOARD_TIMEOUT_EN ), + .SCOREBOARD_TIMEOUT_THRESHOLD (SCOREBOARD_TIMEOUT_THRESHOLD ), + .TEST_CASE_NUM_PER_CYCLE(TEST_CASE_NUM_PER_CYCLE ), + .TEST_CASE_SINGLE_ROUTER(1 ), + + .ASSUMED_SYSTEM_FREQUENCY((1<<30) ) + ) + v_test_generator_u ( + .new_test_vld_o (new_test_vld ), + .new_test_o (new_test ), + .new_test_rdy_i (new_test_rdy ), + .src_id_lfsr_seed_i (src_id_lfsr_seed ^ mcycle[16+:32]), + .tgt_id_lfsr_seed_i (tgt_id_lfsr_seed ^ mcycle[20+:32] ), + .lfsr_update_en_i (&mcycle[16-1:0]), + .mcycle_i (mcycle), + .clk (clk ), + .rstn (rstn) + ); + + logic [INPUT_PORT_NUM-1:0] new_scoreboard_entry_vld; + scoreboard_entry_t [INPUT_PORT_NUM-1:0] new_scoreboard_entry; + logic [INPUT_PORT_NUM-1:0] new_scoreboard_entry_rdy; + node_id_t [OUTPUT_PORT_NUM-1:0] sender_node_id; + +// sender0 (1,2) +// | +// sender3 (0,1) - (1,1) - (2,1) sender2 +// | \ +// sender1 (1,0) (local) sender4 + + assign sender_node_id[0].x_position = 1; + assign sender_node_id[0].y_position = 2; + assign sender_node_id[0].device_port = '0; + assign sender_node_id[0].device_id = '0; + assign sender_node_id[1].x_position = 1; + assign sender_node_id[1].y_position = 0; + assign sender_node_id[1].device_port = '0; + assign sender_node_id[1].device_id = '0; + assign sender_node_id[2].x_position = 2; + assign sender_node_id[2].y_position = 1; + assign sender_node_id[2].device_port = '0; + assign sender_node_id[2].device_id = '0; + assign sender_node_id[3].x_position = 0; + assign sender_node_id[3].y_position = 1; + assign sender_node_id[3].device_port = '0; + assign sender_node_id[3].device_id = '0; + generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_local_sender_node_id + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_local_sender_node_id + assign sender_node_id[4+i].x_position = 1; + assign sender_node_id[4+i].y_position = 1; + assign sender_node_id[4+i].device_port = i; + assign sender_node_id[4+i].device_id = '0; + end + end + endgenerate + + v_sender + #( + .FLIT_BUFFER_DEPTH (8 ), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_N), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_N ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_N (1) + ) + v_sender_toN_u ( + .tx_flit_pend_o (rx_flit_pend_i [0] ), + .tx_flit_v_o (rx_flit_v_i [0] ), + .tx_flit_o (rx_flit_i [0] ), + .tx_flit_vc_id_o (rx_flit_vc_id_i [0] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing_i [0] ), + + .tx_lcrd_v_i (rx_lcrd_v_o [0] ), + .tx_lcrd_id_i (rx_lcrd_id_o [0] ), + + .new_test_vld_i (new_test_vld [0] ), + .new_test_i (new_test [0] ), + .new_test_rdy_o (new_test_rdy [0] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [0] ), + .new_scoreboard_entry_o (new_scoreboard_entry [0] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [0] ), + + .node_id_i (sender_node_id [0] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + + v_sender + #( + .FLIT_BUFFER_DEPTH (8 ), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_S), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_S ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_S (1) + ) + v_sender_toS_u ( + .tx_flit_pend_o (rx_flit_pend_i [1] ), + .tx_flit_v_o (rx_flit_v_i [1] ), + .tx_flit_o (rx_flit_i [1] ), + .tx_flit_vc_id_o (rx_flit_vc_id_i [1] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing_i [1] ), + + .tx_lcrd_v_i (rx_lcrd_v_o [1] ), + .tx_lcrd_id_i (rx_lcrd_id_o [1] ), + + .new_test_vld_i (new_test_vld [1] ), + .new_test_i (new_test [1] ), + .new_test_rdy_o (new_test_rdy [1] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [1] ), + .new_scoreboard_entry_o (new_scoreboard_entry [1] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [1] ), + + .node_id_i (sender_node_id [1] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + + v_sender + #( + .FLIT_BUFFER_DEPTH (8 ), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_E), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_E ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_E (1) + ) + v_sender_toE_u ( + .tx_flit_pend_o (rx_flit_pend_i [2] ), + .tx_flit_v_o (rx_flit_v_i [2] ), + .tx_flit_o (rx_flit_i [2] ), + .tx_flit_vc_id_o (rx_flit_vc_id_i [2] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing_i [2] ), + + .tx_lcrd_v_i (rx_lcrd_v_o [2] ), + .tx_lcrd_id_i (rx_lcrd_id_o [2] ), + + .new_test_vld_i (new_test_vld [2] ), + .new_test_i (new_test [2] ), + .new_test_rdy_o (new_test_rdy [2] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [2] ), + .new_scoreboard_entry_o (new_scoreboard_entry [2] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [2] ), + + .node_id_i (sender_node_id [2] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + + v_sender + #( + .FLIT_BUFFER_DEPTH (8 ), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_W), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_W ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_W (1) + ) + v_sender_toW_u ( + .tx_flit_pend_o (rx_flit_pend_i [3] ), + .tx_flit_v_o (rx_flit_v_i [3] ), + .tx_flit_o (rx_flit_i [3] ), + .tx_flit_vc_id_o (rx_flit_vc_id_i [3] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing_i [3] ), + + .tx_lcrd_v_i (rx_lcrd_v_o [3] ), + .tx_lcrd_id_i (rx_lcrd_id_o [3] ), + + .new_test_vld_i (new_test_vld [3] ), + .new_test_i (new_test [3] ), + .new_test_rdy_o (new_test_rdy [3] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [3] ), + .new_scoreboard_entry_o (new_scoreboard_entry [3] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [3] ), + + .node_id_i (sender_node_id [3] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + +generate + for(i = 0; i < LOCAL_PORT_NUM; i++) begin + v_sender + #( + .FLIT_BUFFER_DEPTH (8 ), + .flit_payload_t (flit_payload_t ), + .VC_NUM_OUTPORT (VC_NUM_INPUT_L), + .VC_DEPTH_OUTPORT (VC_DEPTH_INPUT_L ), + + .SENDER_TIMEOUT_EN (SENDER_TIMEOUT_EN ), + .SENDER_TIMEOUT_THRESHOLD (SENDER_TIMEOUT_THRESHOLD), + + .OUTPUT_TO_L (1) + ) + v_sender_toL_u ( + .tx_flit_pend_o (rx_flit_pend_i [4+i] ), + .tx_flit_v_o (rx_flit_v_i [4+i] ), + .tx_flit_o (rx_flit_i [4+i] ), + .tx_flit_vc_id_o (rx_flit_vc_id_i [4+i] ), + .tx_flit_look_ahead_routing_o (rx_flit_look_ahead_routing_i [4+i] ), + + .tx_lcrd_v_i (rx_lcrd_v_o [4+i] ), + .tx_lcrd_id_i (rx_lcrd_id_o [4+i] ), + + .new_test_vld_i (new_test_vld [4+i] ), + .new_test_i (new_test [4+i] ), + .new_test_rdy_o (new_test_rdy [4+i] ), + + .new_scoreboard_entry_vld_o (new_scoreboard_entry_vld [4+i] ), + .new_scoreboard_entry_o (new_scoreboard_entry [4+i] ), + .new_scoreboard_entry_rdy_i (new_scoreboard_entry_rdy [4+i] ), + + .node_id_i (sender_node_id [4+i] ), + + .mcycle_i (mcycle), + + .clk (clk ), + .rstn (rstn) + ); + end +endgenerate + + + logic [OUTPUT_PORT_NUM-1:0] check_scoreboard_vld; + receiver_info_t [OUTPUT_PORT_NUM-1:0] check_scoreboard; + logic [OUTPUT_PORT_NUM-1:0] check_scoreboard_rdy; + node_id_t [OUTPUT_PORT_NUM-1:0] receiver_node_id; + +// receiver0 (1,2) +// | +// receiver3 (0,1) - (1,1) - (2,1) receiver2 +// | \ +// receiver1 (1,0) (local) receiver4 + + assign receiver_node_id[0].x_position = 1; + assign receiver_node_id[0].y_position = 2; + assign receiver_node_id[0].device_port = '0; + assign receiver_node_id[0].device_id = '0; + assign receiver_node_id[1].x_position = 1; + assign receiver_node_id[1].y_position = 0; + assign receiver_node_id[1].device_port = '0; + assign receiver_node_id[1].device_id = '0; + assign receiver_node_id[2].x_position = 2; + assign receiver_node_id[2].y_position = 1; + assign receiver_node_id[2].device_port = '0; + assign receiver_node_id[2].device_id = '0; + assign receiver_node_id[3].x_position = 0; + assign receiver_node_id[3].y_position = 1; + assign receiver_node_id[3].device_port = '0; + assign receiver_node_id[3].device_id = '0; + generate + if(LOCAL_PORT_NUM > 0) begin: gen_have_local_receiver_node_id + for(i = 0; i < LOCAL_PORT_NUM; i++) begin: gen_local_receiver_node_id + assign receiver_node_id[4+i].x_position = 1; + assign receiver_node_id[4+i].y_position = 1; + assign receiver_node_id[4+i].device_port = i; + assign receiver_node_id[4+i].device_id = '0; + end + end + endgenerate + + generate + for(i = 0; i < OUTPUT_PORT_NUM; i++) begin: gen_v_receiver + v_receiver + #( + .flit_payload_t (flit_payload_t ) + ) + v_receiver_u ( + .rx_flit_pend_i (tx_flit_pend_o [i] ), + .rx_flit_v_i (tx_flit_v_o [i] ), + .rx_flit_i (tx_flit_o [i] ), + .rx_flit_vc_id_i (tx_flit_vc_id_o [i] ), + .rx_flit_look_ahead_routing_i (tx_flit_look_ahead_routing_o [i] ), + + .rx_lcrd_v_o (tx_lcrd_v_i [i] ), + .rx_lcrd_id_o (tx_lcrd_id_i [i] ), + + .check_scoreboard_vld_o (check_scoreboard_vld [i] ), + .check_scoreboard_o (check_scoreboard [i] ), + .check_scoreboard_rdy_i (check_scoreboard_rdy [i] ), + + .node_id_i (receiver_node_id [i] ), + + .clk (clk ), + .rstn (rstn) + ); + end + endgenerate + + + v_scoreboard + #( + .SCOREBOARD_ENTRY_NUM_PER_SENDER (SCOREBOARD_ENTRY_NUM_PER_SENDER ), + .SENDER_NUM (INPUT_PORT_NUM ), + .RECEIVER_NUM (OUTPUT_PORT_NUM ), + + .NODE_NUM_X_DIMESION (NODE_NUM_X_DIMESION ), + .NODE_NUM_Y_DIMESION (NODE_NUM_Y_DIMESION ), + .LOCAL_PORT_NUM (LOCAL_PORT_NUM ), + + .TEST_CASE_SINGLE_ROUTER (1), + + .ASSUMED_SYSTEM_FREQUENCY ((1<<30) ) + ) + v_scoreboard_u ( + .new_scoreboard_entry_vld_i (new_scoreboard_entry_vld ), + .new_scoreboard_entry_i (new_scoreboard_entry ), + .new_scoreboard_entry_rdy_o (new_scoreboard_entry_rdy ), + + .check_scoreboard_vld_i (check_scoreboard_vld ), + .check_scoreboard_i (check_scoreboard ), + .check_scoreboard_rdy_o (check_scoreboard_rdy ), + + .mcycle_i (mcycle ), + + .clk (clk ), + .rstn (rstn) + ); + + + + + + //clock generate + initial begin + clk = 1'b0; + forever #5 clk = ~clk; + end + + //reset generate + initial begin + rstn = 1'b0; + #30; + rstn = 1'b1; + end + + initial begin + int dumpon = 1; + int vcdplus = 0; + $value$plusargs("dumpon=%d", dumpon); + $value$plusargs("vcdplus=%d", vcdplus); + + if (dumpon > 0) begin + $fsdbDumpvars(0, tb_single_router); + $fsdbDumpvars("+struct"); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon; + end + if (vcdplus > 0) begin + $vcdpluson(); + end + end + +endmodule diff --git a/tb/testbench.sv b/tb/testbench.sv new file mode 100644 index 0000000..6afb7e2 --- /dev/null +++ b/tb/testbench.sv @@ -0,0 +1,174 @@ +module testbench +import rvh_noc_pkg::*; +#( + // Parameters + parameter INPUT_PORT_NUM = 5, + parameter OUTPUT_PORT_NUM = 5, + parameter type flit_payload_t = logic[FLIT_LENGTH-1:0], + parameter VC_NUM_INPUT_N = 2, + parameter VC_NUM_INPUT_S = 2, + parameter VC_NUM_INPUT_E = 4, + parameter VC_NUM_INPUT_W = 4, + parameter VC_NUM_INPUT_L = 4, + parameter SA_GLOBAL_INPUT_NUM_N = 4, + parameter SA_GLOBAL_INPUT_NUM_S = 4, + parameter SA_GLOBAL_INPUT_NUM_E = 2, + parameter SA_GLOBAL_INPUT_NUM_W = 2, + parameter SA_GLOBAL_INPUT_NUM_L = 4, + parameter VC_NUM_OUTPUT_N = 2, + parameter VC_NUM_OUTPUT_S = 2, + parameter VC_NUM_OUTPUT_E = 4, + parameter VC_NUM_OUTPUT_W = 4, + parameter VC_NUM_OUTPUT_L = 1, + parameter VC_DEPTH_INPUT_N = 2, + parameter VC_DEPTH_INPUT_S = 2, + parameter VC_DEPTH_INPUT_E = 2, + parameter VC_DEPTH_INPUT_W = 2, + parameter VC_DEPTH_INPUT_L = 2 +) +( + +); + + // Ports + logic [INPUT_PORT_NUM-1:0] rx_flit_pend_i; + logic [INPUT_PORT_NUM-1:0] rx_flit_v_i; + flit_payload_t [INPUT_PORT_NUM-1:0] rx_flit_i; + io_port_t [INPUT_PORT_NUM-1:0] rx_flit_vc_id_i; + io_port_t [INPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing_i; + + logic [OUTPUT_PORT_NUM-1:0] tx_flit_pend_o; + logic [OUTPUT_PORT_NUM-1:0] tx_flit_v_o; + flit_payload_t [OUTPUT_PORT_NUM-1:0] tx_flit_o; + io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_vc_id_o; + io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing_o; + + logic [INPUT_PORT_NUM-1:0] rx_lcrd_v_o; + logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o; + + logic [OUTPUT_PORT_NUM-1:0] tx_lcrd_v_i; + logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i; + + logic [NodeID_X_Width-1:0] node_id_x_ths_hop_i; + logic [NodeID_Y_Width-1:0] node_id_y_ths_hop_i; + + logic clk; + logic rstn; + + vnet_router + #( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + .flit_payload_t(flit_payload_t), + .VC_NUM_INPUT_N(VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S(VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E(VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W(VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L(VC_NUM_INPUT_L ), + .SA_GLOBAL_INPUT_NUM_N(SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S(SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E(SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W(SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L(SA_GLOBAL_INPUT_NUM_L ), + .VC_NUM_OUTPUT_N(VC_NUM_OUTPUT_N ), + .VC_NUM_OUTPUT_S(VC_NUM_OUTPUT_S ), + .VC_NUM_OUTPUT_E(VC_NUM_OUTPUT_E ), + .VC_NUM_OUTPUT_W(VC_NUM_OUTPUT_W ), + .VC_NUM_OUTPUT_L(VC_NUM_OUTPUT_L ), + .VC_DEPTH_INPUT_N(VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S(VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E(VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W(VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L(VC_DEPTH_INPUT_L ) + ) + vnet_router_dut ( + .rx_flit_pend_i (rx_flit_pend_i ), + .rx_flit_v_i (rx_flit_v_i ), + .rx_flit_i (rx_flit_i ), + .rx_flit_vc_id_i (rx_flit_vc_id_i ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i ), + .tx_flit_pend_o (tx_flit_pend_o ), + .tx_flit_v_o (tx_flit_v_o ), + .tx_flit_o (tx_flit_o ), + .tx_flit_vc_id_o (tx_flit_vc_id_o ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing_o ), + .rx_lcrd_v_o (rx_lcrd_v_o ), + .rx_lcrd_id_o (rx_lcrd_id_o ), + .tx_lcrd_v_i (tx_lcrd_v_i ), + .tx_lcrd_id_i (tx_lcrd_id_i ), + .node_id_x_ths_hop_i (node_id_x_ths_hop_i ), + .node_id_y_ths_hop_i (node_id_y_ths_hop_i ), + .clk (clk ), + .rstn (rstn) + ); + + // test generate + flit_payload_t counter; + node_id_t target_node; + always_ff @(posedge clk or negedge rstn) begin + if(~rstn) begin + counter <= '0; + end else begin + counter <= counter + 1; + if(counter == 'd1000) begin + $finish(); + end + end + end + + assign node_id_x_ths_hop_i = 2'b01; + assign node_id_y_ths_hop_i = 2'b01; + + assign target_node.x_position = 1; + assign target_node.y_position = 2; + assign target_node.device_port = 0; + assign target_node.device_id = 0; + + always_comb begin + rx_flit_pend_i = '1; + rx_flit_v_i = '0; + rx_flit_i = '0; + rx_flit_vc_id_i = '0; + rx_flit_look_ahead_routing_i = '0; + if(counter[5:0] == '1) begin + rx_flit_v_i [2] = 1'b1; + rx_flit_i [2] = {{(128-7-4){1'b0}}, target_node, {4'b0}}; // x=1,y=2 + rx_flit_vc_id_i [2][1:0] = counter[7:6] ^ counter[9:8]; + rx_flit_look_ahead_routing_i[2] = N; + end + end + + + + //clock generate + initial begin + clk = 1'b0; + forever #5 clk = ~clk; + end + + //reset generate + initial begin + rstn = 1'b0; + #30; + rstn = 1'b1; + end + + initial begin + int dumpon = 1; + int vcdplus = 0; + $value$plusargs("dumpon=%d", dumpon); + $value$plusargs("vcdplus=%d", vcdplus); + + if (dumpon > 0) begin + $fsdbDumpvars(0, testbench); + $fsdbDumpvars("+struct"); + $fsdbDumpvars("+mda"); + $fsdbDumpvars("+all"); + $fsdbDumpon; + end + if (vcdplus > 0) begin + $vcdpluson(); + end + end + +endmodule diff --git a/tb/top_mesh_syn.sv b/tb/top_mesh_syn.sv new file mode 100644 index 0000000..49146f2 --- /dev/null +++ b/tb/top_mesh_syn.sv @@ -0,0 +1,307 @@ +module top_mesh_syn +import rvh_noc_pkg::*; +// import v_noc_pkg::*; +#( + // mesh parameters + parameter NODE_NUM_X_DIMESION = 3, + parameter NODE_NUM_Y_DIMESION = 3, + + // router parameters + parameter INPUT_PORT_NUM = INPUT_PORT_NUMBER, + parameter OUTPUT_PORT_NUM = OUTPUT_PORT_NUMBER, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + parameter type flit_payload_t = logic[FLIT_LENGTH-1:0], + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter VC_NUM_INPUT_L = 4+LOCAL_PORT_NUM-1+QOS_VC_NUM_PER_INPUT, +`else + parameter VC_NUM_INPUT_L = 4+QOS_VC_NUM_PER_INPUT, +`endif + parameter SA_GLOBAL_INPUT_NUM_N = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_S = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_E = 1+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_W = 1+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter SA_GLOBAL_INPUT_NUM_L = 4+LOCAL_PORT_NUM-1, +`else + parameter SA_GLOBAL_INPUT_NUM_L = 4, +`endif + parameter VC_NUM_OUTPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_L = 1, + parameter VC_DEPTH_INPUT_N = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_S = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_E = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_W = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_L = VC_DEPTH_MAX +) +( + output logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] tx_flit_pend_o, + output logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] tx_flit_v_o, + output flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] tx_flit_o, + output logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o, + output io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] tx_flit_look_ahead_routing_o, + + input logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] rx_flit_pend_i, + input logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] rx_flit_v_i, + input flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] rx_flit_i, + input logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id_i, + input io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] rx_flit_look_ahead_routing_i, + + input logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] tx_lcrd_v_i, + input logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i, + + output logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0] rx_lcrd_v_o, + output logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][LOCAL_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o, + + input logic clk, + input logic rst + +); + + genvar i, j, k; + + // Ports + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_pend; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_v; + flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id; + io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_pend; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_v; + flit_payload_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id; + io_port_t [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][OUTPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0] tx_lcrd_v; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id; + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0] rx_lcrd_v; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id; + + + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][NodeID_X_Width-1:0] node_id_x; + logic [NODE_NUM_X_DIMESION-1:0][NODE_NUM_Y_DIMESION-1:0][NodeID_Y_Width-1:0] node_id_y; + + + + + // generate mesh routers + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_mesh_routers_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_mesh_routers_y_dimesion + vnet_router + #( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + .flit_payload_t(flit_payload_t), + .QOS_VC_NUM_PER_INPUT(QOS_VC_NUM_PER_INPUT), + .VC_NUM_INPUT_N(VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S(VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E(VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W(VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L(VC_NUM_INPUT_L ), + .SA_GLOBAL_INPUT_NUM_N(SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S(SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E(SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W(SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L(SA_GLOBAL_INPUT_NUM_L ), + .VC_NUM_OUTPUT_N(VC_NUM_OUTPUT_N ), + .VC_NUM_OUTPUT_S(VC_NUM_OUTPUT_S ), + .VC_NUM_OUTPUT_E(VC_NUM_OUTPUT_E ), + .VC_NUM_OUTPUT_W(VC_NUM_OUTPUT_W ), + .VC_NUM_OUTPUT_L(VC_NUM_OUTPUT_L ), + .VC_DEPTH_INPUT_N(VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S(VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E(VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W(VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L(VC_DEPTH_INPUT_L ) + ) + vnet_router_dut ( + .rx_flit_pend_i (rx_flit_pend [i][j] ), + .rx_flit_v_i (rx_flit_v [i][j] ), + .rx_flit_i (rx_flit [i][j] ), + .rx_flit_vc_id_i (rx_flit_vc_id [i][j] ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing [i][j] ), + + .tx_flit_pend_o (tx_flit_pend [i][j] ), + .tx_flit_v_o (tx_flit_v [i][j] ), + .tx_flit_o (tx_flit [i][j] ), + .tx_flit_vc_id_o (tx_flit_vc_id [i][j] ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing [i][j] ), + + .rx_lcrd_v_o (rx_lcrd_v [i][j] ), + .rx_lcrd_id_o (rx_lcrd_id [i][j] ), + + .tx_lcrd_v_i (tx_lcrd_v [i][j] ), + .tx_lcrd_id_i (tx_lcrd_id [i][j] ), + + .node_id_x_ths_hop_i (node_id_x [i][j] ), + .node_id_y_ths_hop_i (node_id_y [i][j] ), + + .clk (clk ), + .rstn (rst) + ); + end + end + endgenerate + + // assign node id to each router + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_node_id_x_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_node_id_x_y_dimesion + assign node_id_x [i][j] = i; + end + end + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_node_id_y_y_dimesion + for(j = 0; j < NODE_NUM_X_DIMESION; j++) begin: gen_node_id_y_x_dimesion + assign node_id_y [j][i] = i; + end + end + endgenerate + + // connect each router together + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_connect_routers_ns_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION-1; j++) begin: gen_connect_routers_ns_y_dimesion + // connect N inport to S outport + assign rx_flit_pend [i][j][0] = tx_flit_pend [i][j+1][1]; + assign rx_flit_v [i][j][0] = tx_flit_v [i][j+1][1]; + assign rx_flit [i][j][0] = tx_flit [i][j+1][1]; + assign rx_flit_vc_id [i][j][0] = tx_flit_vc_id [i][j+1][1]; + assign rx_flit_look_ahead_routing [i][j][0] = tx_flit_look_ahead_routing [i][j+1][1]; + + assign tx_lcrd_v [i][j][0] = rx_lcrd_v [i][j+1][1]; + assign tx_lcrd_id [i][j][0] = rx_lcrd_id [i][j+1][1]; + + // connect S inport to N outport + assign rx_flit_pend [i][j+1][1] = tx_flit_pend [i][j][0]; + assign rx_flit_v [i][j+1][1] = tx_flit_v [i][j][0]; + assign rx_flit [i][j+1][1] = tx_flit [i][j][0]; + assign rx_flit_vc_id [i][j+1][1] = tx_flit_vc_id [i][j][0]; + assign rx_flit_look_ahead_routing [i][j+1][1] = tx_flit_look_ahead_routing [i][j][0]; + + assign tx_lcrd_v [i][j+1][1] = rx_lcrd_v [i][j][0]; + assign tx_lcrd_id [i][j+1][1] = rx_lcrd_id [i][j][0]; + end + end + endgenerate + + generate + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_connect_routers_ew_x_dimesion + for(j = 0; j < NODE_NUM_X_DIMESION-1; j++) begin: gen_connect_routers_ew_y_dimesion + // connect E inport to W outport + assign rx_flit_pend [j][i][2] = tx_flit_pend [j+1][i][3]; + assign rx_flit_v [j][i][2] = tx_flit_v [j+1][i][3]; + assign rx_flit [j][i][2] = tx_flit [j+1][i][3]; + assign rx_flit_vc_id [j][i][2] = tx_flit_vc_id [j+1][i][3]; + assign rx_flit_look_ahead_routing [j][i][2] = tx_flit_look_ahead_routing [j+1][i][3]; + + assign tx_lcrd_v [j][i][2] = rx_lcrd_v [j+1][i][3]; + assign tx_lcrd_id [j][i][2] = rx_lcrd_id [j+1][i][3]; + + // connect W inport to E outport + assign rx_flit_pend [j+1][i][3] = tx_flit_pend [j][i][2]; + assign rx_flit_v [j+1][i][3] = tx_flit_v [j][i][2]; + assign rx_flit [j+1][i][3] = tx_flit [j][i][2]; + assign rx_flit_vc_id [j+1][i][3] = tx_flit_vc_id [j][i][2]; + assign rx_flit_look_ahead_routing [j+1][i][3] = tx_flit_look_ahead_routing [j][i][2]; + + assign tx_lcrd_v [j+1][i][3] = rx_lcrd_v [j][i][2]; + assign tx_lcrd_id [j+1][i][3] = rx_lcrd_id [j][i][2]; + end + end + endgenerate + + // other unused non-local ports, assign router rx to 0 + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_unused_non_local_ports_x_dimesion + assign rx_flit_pend [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_v [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_vc_id [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign rx_flit_look_ahead_routing [i][NODE_NUM_Y_DIMESION-1][0] = '0; + + assign tx_lcrd_v [i][NODE_NUM_Y_DIMESION-1][0] = '0; + assign tx_lcrd_id [i][NODE_NUM_Y_DIMESION-1][0] = '0; + + + assign rx_flit_pend [i][0][1] = '0; + assign rx_flit_v [i][0][1] = '0; + assign rx_flit [i][0][1] = '0; + assign rx_flit_vc_id [i][0][1] = '0; + assign rx_flit_look_ahead_routing [i][0][1] = '0; + + assign tx_lcrd_v [i][0][1] = '0; + assign tx_lcrd_id [i][0][1] = '0; + end + + for(i = 0; i < NODE_NUM_Y_DIMESION; i++) begin: gen_unused_non_local_ports_y_dimesion + // connect E inport to W outport + assign rx_flit_pend [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_v [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_vc_id [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign rx_flit_look_ahead_routing [NODE_NUM_X_DIMESION-1][i][2] = '0; + + assign tx_lcrd_v [NODE_NUM_X_DIMESION-1][i][2] = '0; + assign tx_lcrd_id [NODE_NUM_X_DIMESION-1][i][2] = '0; + + // connect W inport to E outport + assign rx_flit_pend [0][i][3] = '0; + assign rx_flit_v [0][i][3] = '0; + assign rx_flit [0][i][3] = '0; + assign rx_flit_vc_id [0][i][3] = '0; + assign rx_flit_look_ahead_routing [0][i][3] = '0; + + assign tx_lcrd_v [0][i][3] = '0; + assign tx_lcrd_id [0][i][3] = '0; + end + endgenerate + + + + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_v_sender_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_v_sender_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_v_sender_device_port + assign rx_flit_pend [i][j][4+k] = rx_flit_pend_i [i][j][k]; + assign rx_flit_v [i][j][4+k] = rx_flit_v_i [i][j][k]; + assign rx_flit [i][j][4+k] = rx_flit_i [i][j][k]; + assign rx_flit_vc_id [i][j][4+k] = rx_flit_vc_id_i [i][j][k]; + assign rx_flit_look_ahead_routing [i][j][4+k] = rx_flit_look_ahead_routing_i [i][j][k]; + + assign rx_lcrd_v_o [i][j][k] = rx_lcrd_v [i][j][4+k]; + assign rx_lcrd_id_o [i][j][k] = rx_lcrd_id [i][j][4+k]; + end + end + end + endgenerate + + + generate + for(i = 0; i < NODE_NUM_X_DIMESION; i++) begin: gen_v_receiver_x_dimesion + for(j = 0; j < NODE_NUM_Y_DIMESION; j++) begin: gen_v_receiver_y_dimesion + for(k = 0; k < LOCAL_PORT_NUM; k++) begin: gen_v_sender_device_port + assign tx_flit_pend_o [i][j][k] = tx_flit_pend [i][j][4+k]; + assign tx_flit_v_o [i][j][k] = tx_flit_v [i][j][4+k]; + assign tx_flit_o [i][j][k] = tx_flit [i][j][4+k]; + assign tx_flit_vc_id_o [i][j][k] = tx_flit_vc_id [i][j][4+k]; + assign tx_flit_look_ahead_routing_o [i][j][k] = tx_flit_look_ahead_routing [i][j][4+k]; + + assign tx_lcrd_v [i][j][4+k] = tx_lcrd_v_i [i][j][k]; + assign tx_lcrd_id [i][j][4+k] = tx_lcrd_id_i [i][j][k]; + end + end + end + endgenerate + + + +endmodule diff --git a/tb/top_single_router_syn.sv b/tb/top_single_router_syn.sv new file mode 100644 index 0000000..e4765d3 --- /dev/null +++ b/tb/top_single_router_syn.sv @@ -0,0 +1,121 @@ +module top_single_router_syn +import rvh_noc_pkg::*; +// import v_noc_pkg::*; +#( + // mesh parameters + parameter NODE_NUM_X_DIMESION = 3, + parameter NODE_NUM_Y_DIMESION = 3, + + // router parameters + parameter INPUT_PORT_NUM = INPUT_PORT_NUMBER, + parameter OUTPUT_PORT_NUM = OUTPUT_PORT_NUMBER, + parameter LOCAL_PORT_NUM = INPUT_PORT_NUM-4, + parameter type flit_payload_t = logic[FLIT_LENGTH-1:0], + parameter VC_NUM_INPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_INPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter VC_NUM_INPUT_L = 4+LOCAL_PORT_NUM-1+QOS_VC_NUM_PER_INPUT, +`else + parameter VC_NUM_INPUT_L = 4+QOS_VC_NUM_PER_INPUT, +`endif + parameter SA_GLOBAL_INPUT_NUM_N = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_S = 3+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_E = 1+LOCAL_PORT_NUM, + parameter SA_GLOBAL_INPUT_NUM_W = 1+LOCAL_PORT_NUM, +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + parameter SA_GLOBAL_INPUT_NUM_L = 4+LOCAL_PORT_NUM-1, +`else + parameter SA_GLOBAL_INPUT_NUM_L = 4, +`endif + parameter VC_NUM_OUTPUT_N = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_S = 1+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_E = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_W = 3+LOCAL_PORT_NUM+QOS_VC_NUM_PER_INPUT, + parameter VC_NUM_OUTPUT_L = 1, + parameter VC_DEPTH_INPUT_N = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_S = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_E = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_W = VC_DEPTH_MAX, + parameter VC_DEPTH_INPUT_L = VC_DEPTH_MAX +) +( + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_pend_o, + output logic [OUTPUT_PORT_NUM-1:0] tx_flit_v_o, + output flit_payload_t [OUTPUT_PORT_NUM-1:0] tx_flit_o, + output logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o, + output io_port_t [OUTPUT_PORT_NUM-1:0] tx_flit_look_ahead_routing_o, + + input logic [INPUT_PORT_NUM-1:0] rx_flit_pend_i, + input logic [INPUT_PORT_NUM-1:0] rx_flit_v_i, + input flit_payload_t [INPUT_PORT_NUM-1:0] rx_flit_i, + input logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id_i, + input io_port_t [INPUT_PORT_NUM-1:0] rx_flit_look_ahead_routing_i, + + input logic [OUTPUT_PORT_NUM-1:0] tx_lcrd_v_i, + input logic [OUTPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i, + + output logic [INPUT_PORT_NUM-1:0] rx_lcrd_v_o, + output logic [INPUT_PORT_NUM-1:0][VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o, + + input logic clk, + input logic rst + +); + + // router + vnet_router + #( + .INPUT_PORT_NUM(INPUT_PORT_NUM ), + .OUTPUT_PORT_NUM(OUTPUT_PORT_NUM ), + .flit_payload_t(flit_payload_t), + .QOS_VC_NUM_PER_INPUT(QOS_VC_NUM_PER_INPUT), + .VC_NUM_INPUT_N(VC_NUM_INPUT_N ), + .VC_NUM_INPUT_S(VC_NUM_INPUT_S ), + .VC_NUM_INPUT_E(VC_NUM_INPUT_E ), + .VC_NUM_INPUT_W(VC_NUM_INPUT_W ), + .VC_NUM_INPUT_L(VC_NUM_INPUT_L ), + .SA_GLOBAL_INPUT_NUM_N(SA_GLOBAL_INPUT_NUM_N ), + .SA_GLOBAL_INPUT_NUM_S(SA_GLOBAL_INPUT_NUM_S ), + .SA_GLOBAL_INPUT_NUM_E(SA_GLOBAL_INPUT_NUM_E ), + .SA_GLOBAL_INPUT_NUM_W(SA_GLOBAL_INPUT_NUM_W ), + .SA_GLOBAL_INPUT_NUM_L(SA_GLOBAL_INPUT_NUM_L ), + .VC_NUM_OUTPUT_N(VC_NUM_OUTPUT_N ), + .VC_NUM_OUTPUT_S(VC_NUM_OUTPUT_S ), + .VC_NUM_OUTPUT_E(VC_NUM_OUTPUT_E ), + .VC_NUM_OUTPUT_W(VC_NUM_OUTPUT_W ), + .VC_NUM_OUTPUT_L(VC_NUM_OUTPUT_L ), + .VC_DEPTH_INPUT_N(VC_DEPTH_INPUT_N ), + .VC_DEPTH_INPUT_S(VC_DEPTH_INPUT_S ), + .VC_DEPTH_INPUT_E(VC_DEPTH_INPUT_E ), + .VC_DEPTH_INPUT_W(VC_DEPTH_INPUT_W ), + .VC_DEPTH_INPUT_L(VC_DEPTH_INPUT_L ) + ) + vnet_router_dut ( + .rx_flit_pend_i (rx_flit_pend_i ), + .rx_flit_v_i (rx_flit_v_i ), + .rx_flit_i (rx_flit_i ), + .rx_flit_vc_id_i (rx_flit_vc_id_i ), + .rx_flit_look_ahead_routing_i (rx_flit_look_ahead_routing_i ), + + .tx_flit_pend_o (tx_flit_pend_o ), + .tx_flit_v_o (tx_flit_v_o ), + .tx_flit_o (tx_flit_o ), + .tx_flit_vc_id_o (tx_flit_vc_id_o ), + .tx_flit_look_ahead_routing_o (tx_flit_look_ahead_routing_o ), + + .rx_lcrd_v_o (rx_lcrd_v_o ), + .rx_lcrd_id_o (rx_lcrd_id_o ), + + .tx_lcrd_v_i (tx_lcrd_v_i ), + .tx_lcrd_id_i (tx_lcrd_id_i ), + + .node_id_x_ths_hop_i ('0 ), + .node_id_y_ths_hop_i ('0 ), + + .clk (clk ), + .rstn (rst) + ); + +endmodule diff --git a/tb/v_noc_pkg.sv b/tb/v_noc_pkg.sv new file mode 100644 index 0000000..dfb7d07 --- /dev/null +++ b/tb/v_noc_pkg.sv @@ -0,0 +1,53 @@ +`ifndef __V_NOC_PKG_SV__ +`define __V_NOC_PKG_SV__ + +// `define V_ROUTER_PM_PRINT_EN +`define V_INPORT_PRINT_EN + +package v_noc_pkg; + import rvh_noc_pkg::*; + + localparam SCOREBOARD_TIMEOUT_W = 15; + localparam SENDER_TIMEOUT_W = 15; + localparam FLIT_DATA_LENGTH = FLIT_LENGTH-QoS_Value_Width-$bits(node_id_t)*2-TxnID_Width; + + typedef struct packed { + logic [FLIT_DATA_LENGTH-1:0] flit_data; + flit_dec_t flit_head; + logic [SCOREBOARD_TIMEOUT_W-1:0] timeout_threshold; + logic [64-1:0] mcycle_when_generated; // the cycle when the test case is generated + logic [QoS_Value_Width-1:0] qos_value; + } test_case_t; + + typedef struct packed { + node_id_t tgt_id; // target id + node_id_t src_id; // source id + logic [TxnID_Width-1:0] txn_id; // transaction id + logic [SCOREBOARD_TIMEOUT_W-1:0] timeout_threshold; + io_port_t look_ahead_routing; + logic [VC_ID_NUM_MAX_W-1:0] inport_vc_id; + logic [64-1:0] generated_mcycle; // when it geerated by test generate + logic [64-1:0] sent_mcycle; // when it inject into noc + logic [FLIT_DATA_LENGTH-1:0] flit_data; + logic [QoS_Value_Width-1:0] qos_value; + + // TODO: routing path + } scoreboard_entry_t; + + typedef struct packed { + logic [SCOREBOARD_TIMEOUT_W-1:0] timeout_counter; + } scoreboard_timer_t; + + typedef struct packed { + logic [SENDER_TIMEOUT_W-1:0] timeout_counter; + } sender_timer_t; + + typedef struct packed { + node_id_t rec_id; // receiver id (should be the same as tgt_id) + node_id_t src_id; // source id + logic [TxnID_Width-1:0] txn_id; // transaction id + logic [FLIT_DATA_LENGTH-1:0] flit_data; + } receiver_info_t; + +endpackage +`endif diff --git a/tb/v_receiver.sv b/tb/v_receiver.sv new file mode 100644 index 0000000..42f0b00 --- /dev/null +++ b/tb/v_receiver.sv @@ -0,0 +1,56 @@ +module v_receiver +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + parameter type flit_payload_t = logic[256-1:0] +) +( + // intf with dut + // input from one of dut router's outports // N,S,E,W,L + input logic rx_flit_pend_i, + input logic rx_flit_v_i, + input flit_payload_t rx_flit_i, + input logic [VC_ID_NUM_MAX_W-1:0] rx_flit_vc_id_i, + input io_port_t rx_flit_look_ahead_routing_i, + + // free vc credit from dut + output logic rx_lcrd_v_o, + output logic [VC_ID_NUM_MAX_W-1:0] rx_lcrd_id_o, + + // intf with scoreboard + output logic check_scoreboard_vld_o, + output receiver_info_t check_scoreboard_o, + input logic check_scoreboard_rdy_i, + + // node id + input node_id_t node_id_i, + + input logic clk, + input logic rstn +); + +flit_dec_t flit_ctrl_info; + +input_port_flit_decoder +#( + .flit_payload_t (flit_payload_t) +) +receiver_flit_decoder_u +( + .flit_v_i (rx_flit_v_i ), + .flit_i (rx_flit_i ), + .flit_look_ahead_routing_i(rx_flit_look_ahead_routing_i), + + .flit_dec_o (flit_ctrl_info ) +); + +assign check_scoreboard_vld_o = rx_flit_v_i; +assign check_scoreboard_o.rec_id = node_id_i; +assign check_scoreboard_o.src_id = flit_ctrl_info.src_id; +assign check_scoreboard_o.txn_id = flit_ctrl_info.txn_id; +assign check_scoreboard_o.flit_data = rx_flit_i[FLIT_LENGTH-1-:FLIT_DATA_LENGTH]; + +assign rx_lcrd_v_o = rx_flit_v_i; +assign rx_lcrd_id_o = rx_flit_vc_id_i; + +endmodule diff --git a/tb/v_scoreboard.sv b/tb/v_scoreboard.sv new file mode 100644 index 0000000..84e6c14 --- /dev/null +++ b/tb/v_scoreboard.sv @@ -0,0 +1,467 @@ +module v_scoreboard +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + parameter SCOREBOARD_ENTRY_NUM_PER_SENDER = 8, // size of scoreboard per sender, should be able to hold all inflight transaction, or the verification coverage would be degraged + parameter SCOREBOARD_ENTRY_NUM_PER_SENDER_IDX_W = SCOREBOARD_ENTRY_NUM_PER_SENDER > 1 ? $clog2(SCOREBOARD_ENTRY_NUM_PER_SENDER) : 1, + parameter SENDER_NUM = 1, + parameter RECEIVER_NUM = 1, + + parameter NODE_NUM_X_DIMESION = 2, // only used in non TEST_CASE_SINGLE_ROUTER mode + parameter NODE_NUM_Y_DIMESION = 3, // only used in non TEST_CASE_SINGLE_ROUTER mode + parameter LOCAL_PORT_NUM = 1, // only used in non TEST_CASE_SINGLE_ROUTER mode + + parameter TEST_CASE_SINGLE_ROUTER = 0, + + parameter ASSUMED_SYSTEM_FREQUENCY = (1<<30) // 1GHz +) +( + // intf with sender + input logic [SENDER_NUM-1:0] new_scoreboard_entry_vld_i, + input scoreboard_entry_t [SENDER_NUM-1:0] new_scoreboard_entry_i, + output logic [SENDER_NUM-1:0] new_scoreboard_entry_rdy_o, + + // intf with receiver + input logic [RECEIVER_NUM-1:0] check_scoreboard_vld_i, + input receiver_info_t [RECEIVER_NUM-1:0] check_scoreboard_i, + output logic [RECEIVER_NUM-1:0] check_scoreboard_rdy_o, + + // current system cycle + input logic [64-1:0] mcycle_i, + + input logic clk, + input logic rstn +); + +genvar i, j; + +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_entry_vld_d, scoreboard_entry_vld_q; +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_entry_vld_ena; +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_entry_vld_set, scoreboard_entry_vld_clr; + +scoreboard_entry_t [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_entry_d, scoreboard_entry_q; +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_entry_ena; + +scoreboard_timer_t [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_timer_d, scoreboard_timer_q; +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER-1:0] scoreboard_timer_ena; + +// if at least one scoreboard entry is non valid, the new entry can find a slot to place +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_new_scoreboard_entry_rdy_o + assign new_scoreboard_entry_rdy_o[i] = ~(&(scoreboard_entry_vld_q[i])); + end +endgenerate + +// scoreboard allocate new entry +logic [SENDER_NUM-1:0][SCOREBOARD_ENTRY_NUM_PER_SENDER_IDX_W-1:0] sel_sb_ent_idx; +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_set_scoreboard_entry + always_comb begin + sel_sb_ent_idx[i] = 0; + scoreboard_entry_vld_set[i] = '0; + scoreboard_entry_d [i] = '0; + if(new_scoreboard_entry_vld_i[i]) begin// new + for(int j = SCOREBOARD_ENTRY_NUM_PER_SENDER-1; j >= 0; j--) begin + if(~scoreboard_entry_vld_q[i][j] & ~scoreboard_entry_vld_set[i][j]) begin + sel_sb_ent_idx[i] = j; + end + end + + scoreboard_entry_d[i][sel_sb_ent_idx[i]] = new_scoreboard_entry_i[i]; + + if(~scoreboard_entry_vld_q[i][sel_sb_ent_idx[i]]) begin + scoreboard_entry_vld_set[i][sel_sb_ent_idx[i]] = 1'b1; + end + end + end + end +endgenerate + +// scoreboard deallocate old entry +always_comb begin + scoreboard_entry_vld_clr = '0; + for(int i = 0; i < RECEIVER_NUM; i++) begin: gen_clr_scoreboard_entry + if(check_scoreboard_vld_i[i]) begin// new + for(int j = 0; j < SENDER_NUM; j++) begin + for(int k = 0; k < SCOREBOARD_ENTRY_NUM_PER_SENDER; k++) begin + if(scoreboard_entry_vld_q[j][k]) begin + if( + (scoreboard_entry_q[j][k].txn_id == check_scoreboard_i[i].txn_id) & + (scoreboard_entry_q[j][k].src_id == check_scoreboard_i[i].src_id) & + (scoreboard_entry_q[j][k].tgt_id == check_scoreboard_i[i].rec_id) & + (scoreboard_entry_q[j][k].flit_data == check_scoreboard_i[i].flit_data) + ) begin + scoreboard_entry_vld_clr[j][k] = 1'b1; + end + end + end + end + end + end +end + +// scoreboard timer +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_scoreboard_timer_d_i + for(j = 0; j < SCOREBOARD_ENTRY_NUM_PER_SENDER; j++) begin:gen_scoreboard_timer_d_j + assign scoreboard_timer_d [i][j] = scoreboard_entry_vld_set[i][j] ? 0 : scoreboard_timer_q[i][j] + 1; + assign scoreboard_timer_ena[i][j] = scoreboard_entry_vld_set[i][j] | (scoreboard_entry_vld_q[i][j] & ~scoreboard_entry_vld_clr[i][j]); + end + end +endgenerate + +// scoreboard entry change +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_scoreboard_entry_vld_d_i + for(j = 0; j < SCOREBOARD_ENTRY_NUM_PER_SENDER; j++) begin:gen_scoreboard_entry_vld_d_j + assign scoreboard_entry_vld_ena[i][j] = scoreboard_entry_vld_set[i][j] | scoreboard_entry_vld_clr[i][j]; + assign scoreboard_entry_vld_d [i][j] = scoreboard_entry_vld_set[i][j] & ~scoreboard_entry_vld_clr[i][j]; + assign scoreboard_entry_ena [i][j] = scoreboard_entry_vld_set[i][j]; + end + end + +endgenerate + +`ifndef SYNTHESIS + assert property(@(posedge clk)disable iff(~rstn) ((scoreboard_entry_vld_set & scoreboard_entry_vld_clr) == '0)) + else $fatal("v_scoreboard: set and clr scoreboard_entry_vld at the same cycle"); +`endif + +// registers +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_scoreboard_entry_i + for(j = 0; j < SCOREBOARD_ENTRY_NUM_PER_SENDER; j++) begin:gen_scoreboard_entry_j + std_dffre + #(.WIDTH(1)) + U_STA_SCOREBOARD_ENTRY_VLD_REG + ( + .clk(clk), + .rstn(rstn), + .en(scoreboard_entry_vld_ena[i][j]), + .d (scoreboard_entry_vld_d [i][j]), + .q (scoreboard_entry_vld_q [i][j]) + ); + + std_dffe + #(.WIDTH($bits(scoreboard_entry_t))) + U_DAT_SCOREBOARD_ENTRY_REG + ( + .clk(clk), + .en(scoreboard_entry_ena[i][j]), + .d (scoreboard_entry_d [i][j]), + .q (scoreboard_entry_q [i][j]) + ); + + std_dffe + #(.WIDTH($bits(scoreboard_timer_t))) + U_DAT_SCOREBOARD_TIMER_REG + ( + .clk(clk), + .en(scoreboard_timer_ena[i][j]), + .d (scoreboard_timer_d [i][j]), + .q (scoreboard_timer_q [i][j]) + ); + end + end +endgenerate + +// check for receiver 1.target error; 2.data error +logic [RECEIVER_NUM-1:0] find_entry; +always_ff @(posedge clk) begin + find_entry = '0; + for(int i = 0; i < RECEIVER_NUM; i++) begin + if(check_scoreboard_vld_i[i]) begin + for(int j = 0; j < SENDER_NUM; j++) begin + for(int k = 0; k < SCOREBOARD_ENTRY_NUM_PER_SENDER; k++) begin + if(scoreboard_entry_vld_q[j][k]) begin + if((scoreboard_entry_q[j][k].src_id == check_scoreboard_i[i].src_id) && + (scoreboard_entry_q[j][k].txn_id == check_scoreboard_i[i].txn_id) && + (TEST_CASE_SINGLE_ROUTER || + (scoreboard_entry_q[j][k].tgt_id == check_scoreboard_i[i].rec_id) && + (scoreboard_entry_q[j][k].flit_data == check_scoreboard_i[i].flit_data) + ) + ) begin // found the entry + find_entry[i] = 1'b1; + // check target position + if((scoreboard_entry_q[j][k].tgt_id.x_position != check_scoreboard_i[i].rec_id.x_position) | + (scoreboard_entry_q[j][k].tgt_id.y_position != check_scoreboard_i[i].rec_id.y_position) + ) begin + $display("[%16d] error: receiver position mismatch", $time()); + $display("txn_id: 0x%h, sender: %2d (%d,%d), receiver: %d (%d,%d)", + check_scoreboard_i[i].txn_id, + j, check_scoreboard_i[i].src_id.x_position, check_scoreboard_i[i].src_id.y_position, + i, check_scoreboard_i[i].rec_id.x_position, check_scoreboard_i[i].rec_id.y_position); + $display("tgt_id: (%d,%d), tgt_local_port: %d, look_ahead_routing: %d, send_time: %d", + scoreboard_entry_q[j][k].tgt_id.x_position, scoreboard_entry_q[j][k].tgt_id.y_position, scoreboard_entry_q[j][k].tgt_id.device_port, + scoreboard_entry_q[j][k].look_ahead_routing, scoreboard_entry_q[j][k].sent_mcycle); + $finish(); + end + + // check port id if tgt is (one of) the local port(s) of the dut router + if(((check_scoreboard_i[i].rec_id.x_position == 1) && + (check_scoreboard_i[i].rec_id.y_position == 1)) || // assume the dut is (1,1) for single router mode + !TEST_CASE_SINGLE_ROUTER // always check if is mesh mode + ) begin + if(scoreboard_entry_q[j][k].tgt_id.device_port != check_scoreboard_i[i].rec_id.device_port) begin + $display("[%16d] error: receiver local_port_id mismatch", $time()); + $display("txn_id: 0x%h, sender: %2d (%d,%d), sender_local_port: %d; receiver: %d (%d,%d), receiver_local_port: %d", + check_scoreboard_i[i].txn_id, + j, check_scoreboard_i[i].src_id.x_position, check_scoreboard_i[i].src_id.y_position, + check_scoreboard_i[i].src_id.device_port, + i, check_scoreboard_i[i].rec_id.x_position, check_scoreboard_i[i].rec_id.y_position, + check_scoreboard_i[i].rec_id.device_port); + $display("tgt_id: (%d,%d), tgt_local_port: %d, look_ahead_routing: %d, send_time: %d", + scoreboard_entry_q[j][k].tgt_id.x_position, scoreboard_entry_q[j][k].tgt_id.y_position, scoreboard_entry_q[j][k].tgt_id.device_port, + scoreboard_entry_q[j][k].look_ahead_routing, scoreboard_entry_q[j][k].sent_mcycle); + $finish(); + end + end + + // check data + if(scoreboard_entry_q[j][k].flit_data != check_scoreboard_i[i].flit_data) begin + $display("[%16d] error: data mismatch", $time()); + $display("txn_id: 0x%h, sender: %2d (%d,%d), receiver: %d (%d,%d), received_data: %h", + check_scoreboard_i[i].txn_id, + j, check_scoreboard_i[i].src_id.x_position, check_scoreboard_i[i].src_id.y_position, + i, check_scoreboard_i[i].rec_id.x_position, check_scoreboard_i[i].rec_id.y_position, + check_scoreboard_i[i].flit_data); + $display("tgt_id: (%d,%d), tgt_local_port: %d, look_ahead_routing: %d, send_time: %d, sent_data: %h", + scoreboard_entry_q[j][k].tgt_id.x_position, scoreboard_entry_q[j][k].tgt_id.y_position, scoreboard_entry_q[j][k].tgt_id.device_port, + scoreboard_entry_q[j][k].look_ahead_routing, scoreboard_entry_q[j][k].sent_mcycle, + scoreboard_entry_q[j][k].flit_data); + $finish(); + end + end + end + end + end + if(find_entry[i] == 1'b0) begin + $display("[%16d] error: scoreboard failed to find the entry, txn_id: 0x%h, sender: (%d,%d), receiver: (%d,%d)", + $time(), check_scoreboard_i[i].txn_id, + check_scoreboard_i[i].src_id.x_position, check_scoreboard_i[i].src_id.y_position, + check_scoreboard_i[i].rec_id.x_position, check_scoreboard_i[i].rec_id.y_position); + $finish(); + end + end + end +end + +// check for scoreboard timeout +always_ff @(posedge clk) begin + for(int i = 0; i < SENDER_NUM; i++) begin + for(int j = 0; j < SCOREBOARD_ENTRY_NUM_PER_SENDER; j++) begin + if(scoreboard_entry_vld_q[i][j]) begin + if((scoreboard_timer_q[i][j].timeout_counter >= scoreboard_entry_q[i][j].timeout_threshold) + && (scoreboard_entry_q[i][j].timeout_threshold != '0)) begin + $display("[%16d] error: scoreboard entry timeout, timeout_threshold: %d", + $time(), scoreboard_entry_q[i][j].timeout_threshold); + $display("txn_id: 0x%h, sender: %2d (%d,%d), sender_local_port: %d, qos_value = %d", + scoreboard_entry_q[i][j].txn_id, + i, scoreboard_entry_q[i][j].src_id.x_position, scoreboard_entry_q[i][j].src_id.y_position, + scoreboard_entry_q[i][j].src_id.device_port, + scoreboard_entry_q[i][j].qos_value); + $display("tgt_id: (%d,%d), tgt_local_port: %d, look_ahead_routing: %d, send_time: %d", + scoreboard_entry_q[i][j].tgt_id.x_position, scoreboard_entry_q[i][j].tgt_id.y_position, scoreboard_entry_q[i][j].tgt_id.device_port, + scoreboard_entry_q[i][j].look_ahead_routing, scoreboard_entry_q[i][j].sent_mcycle); + if(!((scoreboard_entry_q[i][j].qos_value == '1) && QOS_VC_NUM_PER_INPUT)) begin // if rt flit, don't finish, as its timeout_threshold is too tight + $finish(); + end + end + end + end + end +end + +// average cycle per flit +logic [64-1:0] flit_num_counter_d, flit_num_counter_q; +logic flit_num_counter_ena; +logic [64-1:0] flit_noc_latency_counter_d, flit_noc_latency_counter_q; +logic flit_noc_latency_counter_ena; +logic [64-1:0] flit_app_latency_counter_d, flit_app_latency_counter_q; +logic flit_app_latency_counter_ena; + +// port usage counter +logic [64-1:0] allocate_port_counter_d, allocate_port_counter_q; +logic allocate_port_counter_ena; +logic [64-1:0] deallocate_port_counter_all_d, deallocate_port_counter_all_q; +logic deallocate_port_counter_all_ena; +logic [RECEIVER_NUM-1:0][64-1:0] deallocate_port_counter_d, deallocate_port_counter_q; +logic [RECEIVER_NUM-1:0] deallocate_port_counter_ena; + +std_dffre +#(.WIDTH(64)) +U_DAT_FLIT_NUM_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(flit_num_counter_ena), + .d(flit_num_counter_d), + .q(flit_num_counter_q) +); + +std_dffre +#(.WIDTH(64)) +U_DAT_FLIT_NOC_LATENCY_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(flit_noc_latency_counter_ena), + .d(flit_noc_latency_counter_d), + .q(flit_noc_latency_counter_q) +); + +std_dffre +#(.WIDTH(64)) +U_DAT_FLIT_APP_LATENCY_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(flit_app_latency_counter_ena), + .d(flit_app_latency_counter_d), + .q(flit_app_latency_counter_q) +); + +std_dffre +#(.WIDTH(64)) +U_DAT_ALLOCATE_PORT_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(allocate_port_counter_ena), + .d(allocate_port_counter_d), + .q(allocate_port_counter_q) +); + +std_dffre +#(.WIDTH(64)) +U_DAT_DEALLOCATE_PORT_COUNTER_ALL +( + .clk(clk), + .rstn(rstn), + .en(deallocate_port_counter_all_ena), + .d(deallocate_port_counter_all_d), + .q(deallocate_port_counter_all_q) +); + +generate + for(i = 0; i < RECEIVER_NUM; i++) begin + std_dffre + #(.WIDTH(64)) + U_DAT_DEALLOCATE_PORT_COUNTER + ( + .clk(clk), + .rstn(rstn), + .en(deallocate_port_counter_ena[i]), + .d (deallocate_port_counter_d [i]), + .q (deallocate_port_counter_q [i]) + ); + end +endgenerate + + +// display allocate and deallocate scoreboard entry +real flit_noc_latency_counter; +real flit_app_latency_counter; +real flit_num_counter; + +real allocate_port_counter; +real deallocate_port_counter_all; +real deallocate_port_counter[RECEIVER_NUM-1:0]; + +real mcycle; + +always_ff @(posedge clk) begin + flit_num_counter_d = flit_num_counter_q; + flit_num_counter_ena = 1'b0; + flit_noc_latency_counter_d = flit_noc_latency_counter_q; + flit_noc_latency_counter_ena = 1'b0; + flit_app_latency_counter_d = flit_app_latency_counter_q; + flit_app_latency_counter_ena = 1'b0; + mcycle = mcycle_i; + + allocate_port_counter_d = allocate_port_counter_q; + allocate_port_counter_ena = 1'b0; + deallocate_port_counter_all_d = deallocate_port_counter_all_q; + deallocate_port_counter_all_ena = 1'b0; + + flit_noc_latency_counter = flit_noc_latency_counter_q; + flit_app_latency_counter = flit_app_latency_counter_q; + flit_num_counter = flit_num_counter_q; + + deallocate_port_counter_d = deallocate_port_counter_q; + deallocate_port_counter_ena = '0; + + allocate_port_counter = allocate_port_counter_q; + deallocate_port_counter_all = deallocate_port_counter_all_q; + + for(int i = 0; i < RECEIVER_NUM; i++) begin + deallocate_port_counter[i] = deallocate_port_counter_q[i]; + end + + for(int i = 0; i < SENDER_NUM; i++) begin + for(int j = 0; j < SCOREBOARD_ENTRY_NUM_PER_SENDER; j++) begin + if(scoreboard_entry_vld_set[i][j]) begin + $display("[%16d] info: scoreboard allocate entry, sender: %2d (%d,%d), txn_id: 0x%h, QoS = %d, inport_vc_id:%d, tgt_id: (%d,%d), tgt_local_port: %d, look_ahead_routing: %d, send_data: %h", + $time(), i, + scoreboard_entry_d[i][j].src_id.x_position, scoreboard_entry_d[i][j].src_id.y_position, + scoreboard_entry_d[i][j].txn_id, + scoreboard_entry_d[i][j].qos_value, + scoreboard_entry_d[i][j].inport_vc_id, + scoreboard_entry_d[i][j].tgt_id.x_position, scoreboard_entry_d[i][j].tgt_id.y_position, scoreboard_entry_d[i][j].tgt_id.device_port, + scoreboard_entry_d[i][j].look_ahead_routing, + scoreboard_entry_d[i][j].flit_data); + + allocate_port_counter_d = allocate_port_counter_d + 1; + allocate_port_counter_ena = 1'b1; + end + + if(scoreboard_entry_vld_clr[i][j]) begin + $display("[%16d] info: scoreboard deallocate entry, sender: %2d (%d,%d), txn_id: 0x%h, QoS = %d, inport_vc_id:%d, tgt_id: (%d,%d), tgt_local_port: %d, send_data: %h, [noc_latency: %4d], [app_latency: %4d], [receiver (%d,%d) port %d average_noc_bandwidth: %fGBps])", + $time(), i, + scoreboard_entry_q[i][j].src_id.x_position, scoreboard_entry_q[i][j].src_id.y_position, + scoreboard_entry_q[i][j].txn_id, + scoreboard_entry_q[i][j].qos_value, + scoreboard_entry_q[i][j].inport_vc_id, + scoreboard_entry_q[i][j].tgt_id.x_position, scoreboard_entry_q[i][j].tgt_id.y_position, scoreboard_entry_q[i][j].tgt_id.device_port, + scoreboard_entry_q[i][j].flit_data, + mcycle_i - scoreboard_entry_q[i][j].sent_mcycle, + mcycle_i - scoreboard_entry_q[i][j].generated_mcycle, + + scoreboard_entry_q[i][j].tgt_id.x_position, scoreboard_entry_q[i][j].tgt_id.y_position, scoreboard_entry_q[i][j].tgt_id.device_port, + ((deallocate_port_counter[scoreboard_entry_q[i][j].tgt_id.x_position*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + + scoreboard_entry_q[i][j].tgt_id.y_position*LOCAL_PORT_NUM + + scoreboard_entry_q[i][j].tgt_id.device_port] * + FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY)) + ); + + flit_num_counter_d = flit_num_counter_d + 1; + flit_num_counter_ena = 1'b1; + flit_noc_latency_counter_d = flit_noc_latency_counter_d + (mcycle_i - scoreboard_entry_q[i][j].sent_mcycle); + flit_noc_latency_counter_ena = 1'b1; + flit_app_latency_counter_d = flit_app_latency_counter_d + (mcycle_i - scoreboard_entry_q[i][j].generated_mcycle); + flit_app_latency_counter_ena = 1'b1; + + deallocate_port_counter_all_d = deallocate_port_counter_all_d + 1; + deallocate_port_counter_all_ena = 1'b1; + + // receiver id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id + deallocate_port_counter_d [scoreboard_entry_q[i][j].tgt_id.x_position*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + + scoreboard_entry_q[i][j].tgt_id.y_position*LOCAL_PORT_NUM + + scoreboard_entry_q[i][j].tgt_id.device_port] += 1; + deallocate_port_counter_ena[scoreboard_entry_q[i][j].tgt_id.x_position*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + + scoreboard_entry_q[i][j].tgt_id.y_position*LOCAL_PORT_NUM + + scoreboard_entry_q[i][j].tgt_id.device_port] = 1'b1; + end + end + end + if(|scoreboard_entry_vld_clr) begin + $display("[%16d] info: scoreboard deallocate entry, receiver:all, [average_noc_latency: %f], [average_app_latency: %f], [average_noc_bandwidth: %fGBps]", + $time(), + (flit_noc_latency_counter)/(flit_num_counter), + (flit_app_latency_counter)/(flit_num_counter), + ((deallocate_port_counter_all * FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY))); + end +end + + +endmodule diff --git a/tb/v_sender.sv b/tb/v_sender.sv new file mode 100644 index 0000000..400a95d --- /dev/null +++ b/tb/v_sender.sv @@ -0,0 +1,198 @@ +module v_sender +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + parameter FLIT_BUFFER_DEPTH = 8, + parameter type flit_payload_t = logic[256-1:0], + + parameter VC_NUM_OUTPORT = 2, + parameter VC_NUM_OUTPORT_IDX_W = VC_NUM_OUTPORT > 1 ? $clog2(VC_NUM_OUTPORT) : 1, + parameter VC_DEPTH_OUTPORT = 2, + parameter VC_DEPTH_OUTPORT_COUNTER_W = $clog2(VC_DEPTH_OUTPORT + 1), + + parameter SENDER_TIMEOUT_EN = 1, + parameter SENDER_TIMEOUT_THRESHOLD = 64, + + parameter OUTPUT_TO_N = 0, + parameter OUTPUT_TO_S = 0, + parameter OUTPUT_TO_E = 0, + parameter OUTPUT_TO_W = 0, + parameter OUTPUT_TO_L = 0 +) +( + // intf with dut + // output to one of dut router's inports // N,S,E,W,L + output logic tx_flit_pend_o, + output logic tx_flit_v_o, + output flit_payload_t tx_flit_o, + output logic [VC_ID_NUM_MAX_W-1:0] tx_flit_vc_id_o, + output io_port_t tx_flit_look_ahead_routing_o, + + // free vc credit from dut + input logic tx_lcrd_v_i, + input logic [VC_ID_NUM_MAX_W-1:0] tx_lcrd_id_i, + + // intf with test generator + input logic new_test_vld_i, + input test_case_t new_test_i, + output logic new_test_rdy_o, + + // intf with scoreboard + output logic new_scoreboard_entry_vld_o, + output scoreboard_entry_t new_scoreboard_entry_o, + input logic new_scoreboard_entry_rdy_i, + + // node id + input node_id_t node_id_i, + + // system cycle counter + input logic [64-1:0] mcycle_i, + + + input logic clk, + input logic rstn +); + +genvar i; + + +// to send flit buffer +logic flit_buffer_head_vld; +test_case_t flit_buffer_head; + +logic flit_buffer_dequeue_vld; + +mp_fifo +#( + .payload_t (test_case_t), + .ENQUEUE_WIDTH (1), + .DEQUEUE_WIDTH (1), + .DEPTH (FLIT_BUFFER_DEPTH), + .MUST_TAKEN_ALL (1) +) +FLIT_BUFFER_U +( + // Enqueue + .enqueue_vld_i (new_test_vld_i ), + .enqueue_payload_i (new_test_i ), + .enqueue_rdy_o (new_test_rdy_o ), + // Dequeue + .dequeue_vld_o (flit_buffer_head_vld ), + .dequeue_payload_o (flit_buffer_head ), + .dequeue_rdy_i (flit_buffer_dequeue_vld ), + + .flush_i (1'b0 ), + + .clk (clk), + .rst (~rstn) +); + + +// credit based flow control +io_port_t look_ahead_routing_test; +logic free_credit_vld; +logic [VC_NUM_OUTPORT_IDX_W-1:0] free_credit_vc_id; +logic flit_vld; + +assign flit_vld = flit_buffer_head_vld & new_scoreboard_entry_rdy_i; + +local_port_couple_module +#( + .VC_NUM_OUTPORT (VC_NUM_OUTPORT ), + .VC_DEPTH_OUTPORT (VC_DEPTH_OUTPORT ), + + .OUTPUT_TO_N (OUTPUT_TO_N), + .OUTPUT_TO_S (OUTPUT_TO_S), + .OUTPUT_TO_E (OUTPUT_TO_E), + .OUTPUT_TO_W (OUTPUT_TO_W), + .OUTPUT_TO_L (OUTPUT_TO_L) +) +local_port_couple_module_u ( + // input vc head to calculate next routing + .node_id_x_tgt_i (flit_buffer_head.flit_head.tgt_id.x_position ), + .node_id_y_tgt_i (flit_buffer_head.flit_head.tgt_id.y_position ), +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + .device_port_tgt_i (flit_buffer_head.flit_head.tgt_id.device_port), +`endif + // input this hop xy addr + .node_id_x_src_i (flit_buffer_head.flit_head.src_id.x_position ), + .node_id_y_src_i (flit_buffer_head.flit_head.src_id.y_position ), + // output look ahead routing result + .look_ahead_routing_o (look_ahead_routing_test ), + + // free credit in from router + .tx_lcrd_v_i (tx_lcrd_v_i ), + .tx_lcrd_id_i (tx_lcrd_id_i ), + // consume credit + .flit_vld_i (flit_vld ), + .flit_qos_value_i (flit_buffer_head.qos_value ), + .free_credit_vld_o (free_credit_vld ), + .free_credit_vc_id_o (free_credit_vc_id), + + .clk (clk ), + .rstn (rstn) +); + +`ifndef SYNTHESIS + assert property(@(posedge clk)disable iff(~rstn) ((flit_vld) |-> (look_ahead_routing_test == tx_flit_look_ahead_routing_o))) + else $fatal("look_ahead_routing_test not equal"); +`endif + +assign flit_buffer_dequeue_vld = free_credit_vld & flit_vld; + +// output to dut +assign tx_flit_pend_o = 1'b1; +assign tx_flit_v_o = flit_buffer_dequeue_vld; +assign tx_flit_o = {flit_buffer_head.flit_data, + flit_buffer_head.flit_head.txn_id, + flit_buffer_head.flit_head.src_id, + flit_buffer_head.flit_head.tgt_id, + flit_buffer_head.qos_value}; +assign tx_flit_vc_id_o = {{(VC_ID_NUM_MAX_W-VC_NUM_OUTPORT_IDX_W){1'b0}}, free_credit_vc_id}; +assign tx_flit_look_ahead_routing_o = flit_buffer_head.flit_head.look_ahead_routing; + +// output to scoreboard +assign new_scoreboard_entry_vld_o = tx_flit_v_o; +assign new_scoreboard_entry_o.tgt_id = flit_buffer_head.flit_head.tgt_id; +assign new_scoreboard_entry_o.src_id = flit_buffer_head.flit_head.src_id; +assign new_scoreboard_entry_o.txn_id = flit_buffer_head.flit_head.txn_id; +assign new_scoreboard_entry_o.timeout_threshold = flit_buffer_head.timeout_threshold; +assign new_scoreboard_entry_o.look_ahead_routing = flit_buffer_head.flit_head.look_ahead_routing; +assign new_scoreboard_entry_o.inport_vc_id = tx_flit_vc_id_o; +assign new_scoreboard_entry_o.generated_mcycle = flit_buffer_head.mcycle_when_generated; +assign new_scoreboard_entry_o.sent_mcycle = mcycle_i; +assign new_scoreboard_entry_o.flit_data = flit_buffer_head.flit_data; +assign new_scoreboard_entry_o.qos_value = flit_buffer_head.qos_value; + +// check for sender timeout +sender_timer_t sender_timer_d, sender_timer_q; +logic sender_timer_ena; + +always_ff @(posedge clk) begin + if((sender_timer_q.timeout_counter >= SENDER_TIMEOUT_THRESHOLD) && SENDER_TIMEOUT_EN) begin + $display("[%d] error: sender timeout, sender: (%d,%d), sender_local_device_port: %d, timeout_threshold: %d", + $time(), + node_id_i.x_position, node_id_i.y_position, node_id_i.device_port, + SENDER_TIMEOUT_THRESHOLD); + $finish(); + end +end + +assign sender_timer_d.timeout_counter = tx_flit_v_o ? '0 : + sender_timer_q.timeout_counter + 1; + +assign sender_timer_ena = 1'b1; + +std_dffre +#(.WIDTH($bits(scoreboard_timer_t))) +U_DAT_SENDER_TIMER_REG +( + .clk(clk), + .rstn(rstn), + .en(sender_timer_ena), + .d (sender_timer_d ), + .q (sender_timer_q ) +); + + +endmodule diff --git a/tb/v_test_generator.sv b/tb/v_test_generator.sv new file mode 100644 index 0000000..027f580 --- /dev/null +++ b/tb/v_test_generator.sv @@ -0,0 +1,642 @@ +module v_test_generator +import rvh_noc_pkg::*; +import v_noc_pkg::*; +#( + parameter SENDER_NUM = 1, + parameter LOCAL_PORT_SENDER_NUM = SENDER_NUM-4, + parameter RANDOM_BIT_NUM = 32, // 1 to 32, at least TEST_CASE_NUM_PER_CYCLE*3 + parameter SCOREBOARD_TIMEOUT_EN = 1, + parameter SCOREBOARD_TIMEOUT_THRESHOLD = 64, + + parameter TEST_CASE_NUM_PER_CYCLE = 1, + + parameter TEST_CASE_SINGLE_ROUTER = 0, // assume the dut router posetion is (1,1) + // its adjacent routers are: + // sender0 (1,2) + // | + // sender3 (0,1) - (1,1) - (2,1) sender2 + // | \ + // sender1 (1,0) (local) sender4 + parameter TEST_CASE_MESH_RANDOM = 0, // random sender and receiver + parameter TEST_CASE_MESH_DIAGONAL = 0, // from (0,0) to (NODE_NUM_X_DIMESION-1, NODE_NUM_Y_DIMESION-1) + parameter NODE_NUM_X_DIMESION = 2, // only used in TEST_CASE_MESH_* mode + parameter NODE_NUM_Y_DIMESION = 3, // only used in TEST_CASE_MESH_* mode + parameter LOCAL_PORT_NUM = 1, // only used in TEST_CASE_MESH_* mode + + parameter ASSUMED_SYSTEM_FREQUENCY = (1<<30) // 1GHz + +) +( + // intf with sender + output logic [SENDER_NUM-1:0] new_test_vld_o, + output test_case_t [SENDER_NUM-1:0] new_test_o, + input logic [SENDER_NUM-1:0] new_test_rdy_i, + + // random seeds + input logic [RANDOM_BIT_NUM-1:0] src_id_lfsr_seed_i, + input logic [RANDOM_BIT_NUM-1:0] tgt_id_lfsr_seed_i, + input logic lfsr_update_en_i, + + input logic [64-1:0] mcycle_i, + + input logic clk, + input logic rstn +); + +genvar i; + +logic [RANDOM_BIT_NUM-1:0] src_id_lfsr_data; +logic [RANDOM_BIT_NUM-1:0] tgt_id_lfsr_data; + +logic [TxnID_Width-1:0] txn_counter; + +logic [TEST_CASE_NUM_PER_CYCLE-1:0] new_test_vld; +test_case_t [TEST_CASE_NUM_PER_CYCLE-1:0] new_test; + +// map new test case to different sender +generate + if(TEST_CASE_SINGLE_ROUTER) begin: gen_map_test_case_single_router + always_comb begin + new_test_vld_o = '0; + new_test_o = '0; + for(int i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin + unique case({new_test[i].flit_head.src_id.x_position, new_test[i].flit_head.src_id.y_position}) + 4'b0110: begin // sender0 (1,2) + new_test_vld_o[0] = new_test_vld[i]; + new_test_o [0] = new_test[i]; + end + 4'b0100: begin // sender1 (1,0) + new_test_vld_o[1] = new_test_vld[i]; + new_test_o [1] = new_test[i]; + end + 4'b1001: begin // sender2 (2,1) + new_test_vld_o[2] = new_test_vld[i]; + new_test_o [2] = new_test[i]; + end + 4'b0001: begin // sender3 (0,1) + new_test_vld_o[3] = new_test_vld[i]; + new_test_o [3] = new_test[i]; + end + 4'b0101: begin // sender4 (1,1) + new_test_vld_o[4+new_test[i].flit_head.src_id.device_port] = new_test_vld[i]; + new_test_o [4+new_test[i].flit_head.src_id.device_port] = new_test[i]; + end + default:begin + $fatal("test generator source id error"); + end + endcase + end + end + end + + + + else if(TEST_CASE_MESH_RANDOM || TEST_CASE_MESH_DIAGONAL) begin: gen_map_test_case_mesh_x + // sender id = x_posotion*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + y_posotion*LOCAL_PORT_NUM + local_port_id + always_comb begin + new_test_vld_o = '0; + new_test_o = '0; + for(int i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin + new_test_vld_o[new_test[i].flit_head.src_id.x_position*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + + new_test[i].flit_head.src_id.y_position*LOCAL_PORT_NUM + + new_test[i].flit_head.src_id.device_port] = new_test_vld[i]; + new_test_o [new_test[i].flit_head.src_id.x_position*(NODE_NUM_Y_DIMESION*LOCAL_PORT_NUM) + + new_test[i].flit_head.src_id.y_position*LOCAL_PORT_NUM + + new_test[i].flit_head.src_id.device_port] = new_test[i]; + + end + end + end +endgenerate + +// generate new test case +generate + if(TEST_CASE_SINGLE_ROUTER) begin: gen_test_case_single_router + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test + always_comb begin + unique case(src_id_lfsr_data[i*3+:3]) + 3'b000: begin // from N + new_test[i].flit_head.src_id.x_position = 1; + new_test[i].flit_head.src_id.y_position = 2; + unique case(tgt_id_lfsr_data[i]) + 1'b0: begin // to S + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 0; + end + default: begin // to L + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 1; + end + endcase + end + 3'b001: begin // from S + new_test[i].flit_head.src_id.x_position = 1; + new_test[i].flit_head.src_id.y_position = 0; + unique case(tgt_id_lfsr_data[i]) + 1'b0: begin // to N + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 2; + end + default: begin // to L + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 1; + end + endcase + end + 3'b010: begin // from E + new_test[i].flit_head.src_id.x_position = 2; + new_test[i].flit_head.src_id.y_position = 1; + unique case(tgt_id_lfsr_data[i*2+:2]) + 2'b00: begin // to N + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 2; + end + 2'b01: begin // to S + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 0; + end + 2'b10: begin // to W + new_test[i].flit_head.tgt_id.x_position = 0; + new_test[i].flit_head.tgt_id.y_position = 1; + end + default: begin // to L + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 1; + end + endcase + end + 3'b011: begin // from W + new_test[i].flit_head.src_id.x_position = 0; + new_test[i].flit_head.src_id.y_position = 1; + unique case(tgt_id_lfsr_data[i*2+:2]) + 2'b00: begin // to N + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 2; + end + 2'b01: begin // to S + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 0; + end + 2'b10: begin // to E + new_test[i].flit_head.tgt_id.x_position = 2; + new_test[i].flit_head.tgt_id.y_position = 1; + end + default: begin // to L + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 1; + end + endcase + end + default: begin // from L + new_test[i].flit_head.src_id.x_position = 1; + new_test[i].flit_head.src_id.y_position = 1; + unique case(tgt_id_lfsr_data[i*2+:2]) + 2'b00: begin // to N + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 2; + end + 2'b01: begin // to S + new_test[i].flit_head.tgt_id.x_position = 1; + new_test[i].flit_head.tgt_id.y_position = 0; + end + 2'b10: begin // to E + new_test[i].flit_head.tgt_id.x_position = 2; + new_test[i].flit_head.tgt_id.y_position = 1; + end + default: begin // to W + new_test[i].flit_head.tgt_id.x_position = 0; + new_test[i].flit_head.tgt_id.y_position = 1; + end + endcase + end + endcase + end + + logic [FLIT_DATA_LENGTH-1:0] flit_data_mask; + always_comb begin + flit_data_mask = ~({RANDOM_BIT_NUM{1'b1}} << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]); + new_test[i].flit_data = '1; + new_test[i].flit_data = ((src_id_lfsr_data[RANDOM_BIT_NUM-1:0] ^ i) << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]) | flit_data_mask; + end + + assign new_test[i].flit_head.txn_id = txn_counter + i; + + assign new_test[i].timeout_threshold = SCOREBOARD_TIMEOUT_EN ? SCOREBOARD_TIMEOUT_THRESHOLD : '0; // 0 means no timeout error + + assign new_test[i].mcycle_when_generated = mcycle_i; + + logic [TEST_CASE_NUM_PER_CYCLE-1:0][2-1:0] random_device_port; + assign random_device_port[i] = src_id_lfsr_data[i*2+:2] ^ tgt_id_lfsr_data[i*2+:2]; + always_comb begin + new_test[i].flit_head.src_id.device_port = '0; + new_test[i].flit_head.src_id.device_id = '0; + new_test[i].flit_head.tgt_id.device_port = '0; + new_test[i].flit_head.tgt_id.device_id = '0; + + unique case(random_device_port[i]) // chooose which local port to route from/to(single router mode doesn't have local to local case) + 2'd0: begin + new_test[i].flit_head.src_id.device_port = 0; + new_test[i].flit_head.tgt_id.device_port = 0; + end +`ifdef LOCAL_PORT_NUM_2 + 2'd1: begin + new_test[i].flit_head.src_id.device_port = 1; + new_test[i].flit_head.tgt_id.device_port = 1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + 2'd2: begin + if(LOCAL_PORT_SENDER_NUM >= 3) begin + new_test[i].flit_head.src_id.device_port = 2; + new_test[i].flit_head.tgt_id.device_port = 2; + end else begin + new_test[i].flit_head.src_id.device_port = 1; + new_test[i].flit_head.tgt_id.device_port = 1; + end + end +`endif +`ifdef LOCAL_PORT_NUM_4 + 2'd3: begin + new_test[i].flit_head.src_id.device_port = 3; + new_test[i].flit_head.tgt_id.device_port = 3; + end +`endif + default: begin + new_test[i].flit_head.src_id.device_port = 0; + new_test[i].flit_head.tgt_id.device_port = 0; + end + endcase + end + + end + end + + + + + else if(TEST_CASE_MESH_RANDOM) begin: gen_test_case_mesh_random + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test + always_comb begin + new_test[i].flit_head.src_id.x_position = src_id_lfsr_data[i*3+:3] % NODE_NUM_X_DIMESION; + new_test[i].flit_head.src_id.y_position = src_id_lfsr_data[RANDOM_BIT_NUM-1-i*3-:3] % NODE_NUM_Y_DIMESION; + + new_test[i].flit_head.tgt_id.x_position = tgt_id_lfsr_data[i*3+:3] % NODE_NUM_X_DIMESION; + new_test[i].flit_head.tgt_id.y_position = tgt_id_lfsr_data[RANDOM_BIT_NUM-1-i*3-:3] % NODE_NUM_Y_DIMESION; + end + + + logic [FLIT_DATA_LENGTH-1:0] flit_data_mask; + always_comb begin + flit_data_mask = ~({RANDOM_BIT_NUM{1'b1}} << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]); + new_test[i].flit_data = '1; + new_test[i].flit_data = ((src_id_lfsr_data[RANDOM_BIT_NUM-1:0] ^ i) << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]) | flit_data_mask; + end + + assign new_test[i].flit_head.txn_id = txn_counter + i; + +`ifdef COMMON_QOS_EXTRA_RT_VC + assign new_test[i].timeout_threshold = (new_test[i].qos_value == '1) ? 2 * (NODE_NUM_X_DIMESION + NODE_NUM_Y_DIMESION - 1) : + SCOREBOARD_TIMEOUT_EN ? SCOREBOARD_TIMEOUT_THRESHOLD : '0; +`else + assign new_test[i].timeout_threshold = SCOREBOARD_TIMEOUT_EN ? SCOREBOARD_TIMEOUT_THRESHOLD : '0; +`endif + + assign new_test[i].mcycle_when_generated = mcycle_i; + + assign new_test[i].flit_head.src_id.device_id = '0; + assign new_test[i].flit_head.tgt_id.device_id = '0; + + assign new_test[i].flit_head.src_id.device_port = (src_id_lfsr_data[i*2+:2] ^ tgt_id_lfsr_data[RANDOM_BIT_NUM-1-i*2-:2]) % LOCAL_PORT_NUM; + assign new_test[i].flit_head.tgt_id.device_port = (src_id_lfsr_data[RANDOM_BIT_NUM-1-i*2-:2] ^ tgt_id_lfsr_data[i*2+:2]) % LOCAL_PORT_NUM; + end + end + + else if(TEST_CASE_MESH_DIAGONAL) begin: gen_test_case_mesh_diagonal + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test + always_comb begin + new_test[i].flit_head.src_id.x_position = 0; + new_test[i].flit_head.src_id.y_position = 0; + + new_test[i].flit_head.tgt_id.x_position = NODE_NUM_X_DIMESION - 1; + new_test[i].flit_head.tgt_id.y_position = NODE_NUM_Y_DIMESION - 1; + end + + + logic [FLIT_DATA_LENGTH-1:0] flit_data_mask; + always_comb begin + flit_data_mask = ~({RANDOM_BIT_NUM{1'b1}} << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]); + new_test[i].flit_data = '1; + new_test[i].flit_data = ((src_id_lfsr_data[RANDOM_BIT_NUM-1:0] ^ i) << tgt_id_lfsr_data[$clog2(FLIT_DATA_LENGTH-RANDOM_BIT_NUM)-1:0]) | flit_data_mask; + end + + assign new_test[i].flit_head.txn_id = txn_counter + i; + + assign new_test[i].timeout_threshold = SCOREBOARD_TIMEOUT_EN ? SCOREBOARD_TIMEOUT_THRESHOLD : '0; + + assign new_test[i].mcycle_when_generated = mcycle_i; + + assign new_test[i].flit_head.src_id.device_id = '0; + assign new_test[i].flit_head.tgt_id.device_id = '0; + + assign new_test[i].flit_head.src_id.device_port = 0; + assign new_test[i].flit_head.tgt_id.device_port = 0; + end + end +endgenerate + + +generate + if(TEST_CASE_SINGLE_ROUTER) begin: gen_test_case_look_ahead_routing_single_router // the dut router is assume to be (1,1) + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test_look_ahead_routing + always_comb begin + if(new_test[i].flit_head.tgt_id.x_position > 1) begin + new_test[i].flit_head.look_ahead_routing = E; + end else if(new_test[i].flit_head.tgt_id.x_position < 1) begin + new_test[i].flit_head.look_ahead_routing = W; + end else if(new_test[i].flit_head.tgt_id.y_position > 1) begin + new_test[i].flit_head.look_ahead_routing = N; + end else if(new_test[i].flit_head.tgt_id.y_position < 1) begin + new_test[i].flit_head.look_ahead_routing = S; + end else begin + unique case(new_test[i].flit_head.tgt_id.device_port) // chooose which local port to route to + 0: begin + new_test[i].flit_head.look_ahead_routing = L0; + end +`ifdef LOCAL_PORT_NUM_2 + 1: begin + new_test[i].flit_head.look_ahead_routing = L1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + 2: begin + new_test[i].flit_head.look_ahead_routing = L2; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + 3: begin + new_test[i].flit_head.look_ahead_routing = L3; + end +`endif + default: begin + new_test[i].flit_head.look_ahead_routing = L0; + end + endcase + end + end + end + end + + + + + else if(TEST_CASE_MESH_RANDOM || TEST_CASE_MESH_DIAGONAL) begin: gen_test_case_look_ahead_routing_mesh_x + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test_look_ahead_routing + always_comb begin + if(new_test[i].flit_head.tgt_id.x_position > new_test[i].flit_head.src_id.x_position) begin + new_test[i].flit_head.look_ahead_routing = E; + end else if(new_test[i].flit_head.tgt_id.x_position < new_test[i].flit_head.src_id.x_position) begin + new_test[i].flit_head.look_ahead_routing = W; + end else if(new_test[i].flit_head.tgt_id.y_position > new_test[i].flit_head.src_id.y_position) begin + new_test[i].flit_head.look_ahead_routing = N; + end else if(new_test[i].flit_head.tgt_id.y_position < new_test[i].flit_head.src_id.y_position) begin + new_test[i].flit_head.look_ahead_routing = S; + end else begin + unique case(new_test[i].flit_head.tgt_id.device_port) // chooose which local port to route to + 0: begin + new_test[i].flit_head.look_ahead_routing = L0; + end +`ifdef LOCAL_PORT_NUM_2 + 1: begin + new_test[i].flit_head.look_ahead_routing = L1; + end +`endif +`ifdef LOCAL_PORT_NUM_3 + 2: begin + new_test[i].flit_head.look_ahead_routing = L2; + end +`endif +`ifdef LOCAL_PORT_NUM_4 + 3: begin + new_test[i].flit_head.look_ahead_routing = L3; + end +`endif + default: begin + new_test[i].flit_head.look_ahead_routing = L0; + end + endcase + end + end + end + end +endgenerate + +logic [TEST_CASE_NUM_PER_CYCLE-1:0][3-1:0] random_qos; +generate + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test_qos_value + assign random_qos[i] = src_id_lfsr_data[i*3+:3] ^~ tgt_id_lfsr_data[i*3+:3]; +`ifdef USE_QOS_VALUE + always_comb begin + unique case(random_qos[i]) + 0, 1: begin + new_test[i].qos_value = 4; + new_test[i].flit_head.qos_value = 4; + end + 2, 3: begin + new_test[i].qos_value = 8; + new_test[i].flit_head.qos_value = 8; + end + 4: begin + new_test[i].qos_value = 15; + new_test[i].flit_head.qos_value = 15; + end + default: begin + new_test[i].qos_value = '0; + new_test[i].flit_head.qos_value = '0; + end + endcase + + // if((new_test[i].flit_head.src_id.x_position == 0) && + // (new_test[i].flit_head.src_id.y_position == 0) && + // (new_test[i].flit_head.src_id.device_port == 0)) begin + // new_test[i].qos_value = '1; + // new_test[i].flit_head.qos_value = '1; + // end + end +`else + assign new_test[i].qos_value = '0; +`endif + end +endgenerate + + + +generate + for(i = 0; i < TEST_CASE_NUM_PER_CYCLE; i++) begin: gen_new_test_vld +`ifdef ALLOW_SAME_ROUTER_L2L_TRANSFER + assign new_test_vld[i] = ~((new_test[i].flit_head.src_id.x_position == new_test[i].flit_head.tgt_id.x_position) && + (new_test[i].flit_head.src_id.y_position == new_test[i].flit_head.tgt_id.y_position) && + (new_test[i].flit_head.src_id.device_port == new_test[i].flit_head.tgt_id.device_port) + ); +`else + assign new_test_vld[i] = (new_test[i].flit_head.src_id.x_position != new_test[i].flit_head.tgt_id.x_position) | + (new_test[i].flit_head.src_id.y_position != new_test[i].flit_head.tgt_id.y_position); +`endif + end +endgenerate + + + +LFSR #(.NUM_BITS(RANDOM_BIT_NUM)) src_id_gen_u ( + .i_Clk (clk), + .i_Enable (rstn), + .i_Seed_DV (lfsr_update_en_i ), + .i_Seed_Data (src_id_lfsr_seed_i), + .o_LFSR_Data (src_id_lfsr_data), + .o_LFSR_Done () +); + +LFSR #(.NUM_BITS(RANDOM_BIT_NUM)) tgt_id_gen_u ( + .i_Clk (clk), + .i_Enable (rstn), + .i_Seed_DV (lfsr_update_en_i ), + .i_Seed_Data (tgt_id_lfsr_seed_i), + .o_LFSR_Data (tgt_id_lfsr_data), + .o_LFSR_Done () +); + +always_ff @(posedge clk or negedge rstn ) begin + if(~rstn) begin + txn_counter <= '0; + end else if(|new_test_vld) begin + txn_counter <= txn_counter + TEST_CASE_NUM_PER_CYCLE; + end +end + + + + + + +// display app throughput + +logic [SENDER_NUM-1:0][64-1:0] flit_num_counter_d, flit_num_counter_q; // sent +logic [SENDER_NUM-1:0] flit_num_counter_ena; +logic [64-1:0] flit_num_counter_all_d, flit_num_counter_all_q; +logic flit_num_counter_all_ena; + +logic [SENDER_NUM-1:0][64-1:0] flit_num_gened_counter_d, flit_num_gened_counter_q; // generated, no matter whether it is sent +logic [SENDER_NUM-1:0] flit_num_gened_counter_ena; +logic [64-1:0] flit_num_gened_counter_all_d, flit_num_gened_counter_all_q; +logic flit_num_gened_counter_all_ena; + + +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_flit_num_counter_q + std_dffre + #(.WIDTH(64)) + U_DAT_FLIT_NUM_COUNTER + ( + .clk(clk), + .rstn(rstn), + .en(flit_num_counter_ena[i]), + .d (flit_num_counter_d [i]), + .q (flit_num_counter_q [i]) + ); + end +endgenerate + +std_dffre +#(.WIDTH(64)) +U_DAT_FLIT_NUM_ALL_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(flit_num_counter_all_ena), + .d (flit_num_counter_all_d ), + .q (flit_num_counter_all_q ) +); + +generate + for(i = 0; i < SENDER_NUM; i++) begin: gen_flit_gened_num_counter_q + std_dffre + #(.WIDTH(64)) + U_DAT_FLIT_GENED_NUM_COUNTER + ( + .clk(clk), + .rstn(rstn), + .en(flit_num_gened_counter_ena[i]), + .d (flit_num_gened_counter_d [i]), + .q (flit_num_gened_counter_q [i]) + ); + end +endgenerate + +std_dffre +#(.WIDTH(64)) +U_DAT_FLIT_GENED_NUM_ALL_COUNTER +( + .clk(clk), + .rstn(rstn), + .en(flit_num_gened_counter_all_ena), + .d (flit_num_gened_counter_all_d ), + .q (flit_num_gened_counter_all_q ) +); + +real flit_num_counter[SENDER_NUM-1:0]; +real flit_num_counter_all; +real flit_num_gened_counter[SENDER_NUM-1:0]; +real flit_num_gened_counter_all; +real mcycle; + +always_ff @(posedge clk) begin + flit_num_counter_all_d = flit_num_counter_all_q; + flit_num_counter_all_ena = 1'b0; + + flit_num_gened_counter_all_d = flit_num_gened_counter_all_q; + flit_num_gened_counter_all_ena = 1'b0; + + mcycle = mcycle_i; + flit_num_counter_all = flit_num_counter_all_q; + flit_num_gened_counter_all = flit_num_gened_counter_all_q; + + for(int i = 0; i < SENDER_NUM; i++) begin + flit_num_counter_d [i] = flit_num_counter_q[i]; + flit_num_counter_ena[i] = 1'b0; + + flit_num_gened_counter_d [i] = flit_num_gened_counter_q[i]; + flit_num_gened_counter_ena[i] = 1'b0; + + flit_num_counter[i] = flit_num_counter_q[i]; + flit_num_gened_counter[i] = flit_num_gened_counter_q[i]; + + if(new_test_vld_o[i]) begin + flit_num_gened_counter_d [i] = flit_num_gened_counter_q[i] + 1; + flit_num_gened_counter_ena[i] = 1'b1; + + flit_num_gened_counter_all_d = flit_num_gened_counter_all_d + 1; + flit_num_gened_counter_all_ena = 1'b1; + + if(new_test_rdy_i[i]) begin + $display("[%16d] info: test_generator gen case to sender: %2d, [average_app_gen_bandwidth: %fGBps], [average_app_bandwidth: %fGBps]", + $time(), i, + ((flit_num_gened_counter[i] * FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY)), + ((flit_num_counter[i] * FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY))); + + flit_num_counter_d [i] = flit_num_counter_q[i] + 1; + flit_num_counter_ena[i] = 1'b1; + + flit_num_counter_all_d = flit_num_counter_all_d + 1; + flit_num_counter_all_ena = 1'b1; + end + end + end + if(|(new_test_vld_o & new_test_rdy_i)) begin + $display("[%16d] info: test_generator gen case to sender:all, [average_app_gen_bandwidth: %fGBps], [average_app_bandwidth: %fGBps]", + $time(), + ((flit_num_gened_counter_all * FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY)), + ((flit_num_counter_all * FLIT_LENGTH /8/1024/1024/1024) / (mcycle / ASSUMED_SYSTEM_FREQUENCY))); + end +end + + +endmodule