├── tcl ├── .gitignore ├── clean_ip_tcl.sh ├── quartus_sim.tcl ├── quartus_lint.tcl ├── README.md ├── quartus_timing.tcl ├── quartus_init.tcl ├── timing.sdc ├── utils.tcl ├── quartus_setup.tcl ├── makefile ├── cdc_fifo.tcl └── phy_rst.tcl ├── tb ├── vpi │ ├── vpi_i │ │ ├── tb_pcs.h │ │ ├── tb_marker.h │ │ ├── tb_marker.c │ │ └── tb_pcs.c │ ├── defs.h │ ├── 64b66b.h │ ├── pcs_tx.h │ ├── tb_config.h │ ├── pcs_enc.h │ ├── pcs_gearbox.h │ ├── vpi_v │ │ ├── tb_marker.hpp │ │ ├── tb_pcs.hpp │ │ ├── tb_marker.cpp │ │ └── tb_pcs.cpp │ ├── tb_marker_common.h │ ├── tb_rand.h │ ├── 64b66b.c │ ├── test.c │ ├── tv.h │ ├── pcs_marker.h │ ├── pcs_gearbox.c │ ├── tb_rand.c │ ├── tb_utils.h │ ├── tb_pcs_common.h │ ├── pcs_enc.c │ ├── tb_marker_common.c │ ├── pcs_tx.c │ ├── tb_fifo.c │ ├── pcs_defs.h │ ├── tb_utils.c │ ├── makefile │ ├── pcs_marker.c │ ├── tv.c │ └── tb_fifo.h ├── am_tx_tb.sv ├── pcs_10g_loopback_tb.sv ├── gearbox_tx_tb.sv ├── _64b66b_tb.sv ├── lane_reorder_rx_tb.sv ├── pcs_10g_enc_tb.sv ├── README.md ├── block_sync_rx_tb.sv ├── gearbox_rx_tb.sv ├── am_lock_rx_tb.sv ├── deskew_rx_tb.sv └── pcs_tb.sv ├── tv ├── Makefile └── main.c ├── .gitignore ├── TODO.md ├── fake_reset.v ├── LICENSE ├── xgmii_pcs_10g_enc.v ├── lane_reorder_rx.v ├── README.md ├── _64b66b_rx.v ├── deskew_rx.v ├── _64b66b_tx.v ├── deskew_lane_rx.v ├── pcs_loopback.v ├── xgmii_pcs_10g_enc_intf.v ├── cy10gx ├── bsp_lite.tcl └── top_pcs.v ├── xgmii_dec_intf_rx.v ├── am_tx.v ├── am_lane_tx.v ├── gearbox_tx.v ├── gearbox_rx.v ├── block_sync_rx.v ├── dec_lite_rx.v ├── am_lock_rx.v └── pcs_enc_lite.v /tcl/.gitignore: -------------------------------------------------------------------------------- 1 | PCS/ 2 | PCS_partitioned_save_view.txt 3 | pdp_design_highlights.tcl 4 | -------------------------------------------------------------------------------- /tcl/clean_ip_tcl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # relax qsys requirement 4 | sed -i 's/package require -exact qsys.*/package require qsys/' $1 5 | 6 | # remove `set_project_property: BOARD` 7 | sed -i 's/set_project_property BOARD .*//' $1 8 | 9 | -------------------------------------------------------------------------------- /tcl/quartus_sim.tcl: -------------------------------------------------------------------------------- 1 | 2 | # simulation files 3 | 4 | set_global_assignment -name EDA_SIMULATION_TOOL "Questa Intel FPGA (Verilog)" 5 | set_global_assignment -name EDA_TIME_SCALE "1 ps" -section_id eda_simulation 6 | set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "VERILOG HDL" -section_id eda_simulation 7 | -------------------------------------------------------------------------------- /tcl/quartus_lint.tcl: -------------------------------------------------------------------------------- 1 | load_package ::quartus::project 2 | load_package ::quartus::flow 3 | 4 | source utils.tcl 5 | 6 | set project_name "PCS" 7 | 8 | # Open project 9 | project_open $project_name.qpf 10 | 11 | # parse files and report lint errors 12 | execute_flow -analysis_and_elaboration 13 | 14 | # Close project 15 | project_close 16 | -------------------------------------------------------------------------------- /tb/vpi/vpi_i/tb_pcs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef TB_PCS 8 | #define TB_PCS 9 | 10 | #endif//TB_PCS 11 | -------------------------------------------------------------------------------- /tv/Makefile: -------------------------------------------------------------------------------- 1 | ifndef debug 2 | #debug := 3 | endif 4 | 5 | FLAGS = -Wall -Wextra -Wconversion -Wshadow -Wundef -fno-common -Wno-unused-parameter -Wno-type-limits 6 | CC = cc $(if $(debug),-DDEBUG -g) 7 | LD = cc 8 | 9 | 64b66b : main.o 10 | $(LD) -o 64b66b -g main.o 11 | 12 | main.o : main.c 13 | $(CC) -c main.c $(FLAGS) 14 | 15 | clean : 16 | rm -f 64b66b *.o 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !Makefile 3 | !doc 4 | !doc/** 5 | !*.v 6 | !*.sv 7 | !.gitignore 8 | !.gitattribute 9 | !.gitmodules 10 | !makefile 11 | !*.jpg 12 | !tcl 13 | !*.tcl 14 | !*.xdc 15 | !tb 16 | !tv 17 | !vpi 18 | !vpi_i 19 | !vpi_v 20 | !*.c 21 | !*.cpp 22 | !*.h 23 | !*.hpp 24 | !*.md 25 | !*.sdc 26 | !cy10gx 27 | !clean_ip_tcl.sh 28 | !*.stp 29 | cy10gx/two_ff_sync.sv 30 | -------------------------------------------------------------------------------- /tb/vpi/vpi_i/tb_marker.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef TB_MARKER_H 8 | #define TB_MARKER_H 9 | 10 | /* iverilog specific vpi code */ 11 | #include 12 | 13 | #endif //TB_MARKER_H 14 | -------------------------------------------------------------------------------- /tb/vpi/defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef DEFS_H 8 | #define DEFS_H 9 | 10 | 11 | #ifdef DEBUG 12 | #include 13 | #define info(...) printf(__VA_ARGS__) 14 | #else 15 | #define info(...) 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /tcl/README.md: -------------------------------------------------------------------------------- 1 | # FPGA build flow 2 | 3 | Build flow target's `quartus` and is by default set to target the intel cyclone 10 GX `10CX150YF780E5G`. 4 | 5 | This project is a work in progress, we are not generating the device configuation file as of today. 6 | The default design implements a single 10GBASE-R PMA+PCS in loopback mode. 7 | 8 | ## Quickstart 9 | 10 | 11 | To create quartus project, generate IPs, run place, route, and generate timming reports: 12 | 13 | ```sh 14 | make build 15 | ``` 16 | 17 | 18 | -------------------------------------------------------------------------------- /tb/vpi/64b66b.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef _64B66B_H 8 | #define _64B66B_H 9 | #include "pcs_defs.h" 10 | 11 | uint64_t scramble(uint64_t *state, uint64_t data, size_t len); 12 | uint64_t descramble(uint64_t *state, uint64_t data, size_t len); 13 | #endif //_64B66B_H 14 | -------------------------------------------------------------------------------- /tb/vpi/pcs_tx.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef PCS_TX_H 9 | #define PCS_TX_H 10 | #include "pcs_defs.h" 11 | 12 | pcs_tx_s *pcs_tx_init(); 13 | 14 | // returns 0 if we accepted the new data 15 | bool get_next_exp(pcs_tx_s *state, const ctrl_lite_s ctrl, block_s *data, block_s *exp); 16 | 17 | #endif//PCS_TX_H 18 | -------------------------------------------------------------------------------- /tb/vpi/tb_config.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_CONFIG_H 9 | #define TB_CONFIG_H 10 | 11 | 12 | #ifndef RAND_SEED 13 | #define RAND_SEED 10 14 | #endif 15 | 16 | // packet length 17 | #define PACKET_LEN_MIN 30 18 | #define PACKET_LEN_MAX 150 19 | 20 | #define PACKET_IDLE_CNT_MIN 2 21 | #define PACKET_IDLE_CNT_MAX 4 22 | 23 | #endif//TB_CONFIG_H 24 | -------------------------------------------------------------------------------- /tcl/quartus_timing.tcl: -------------------------------------------------------------------------------- 1 | load_package ::quartus::project 2 | load_package ::quartus::sdc_ext 3 | 4 | source utils.tcl 5 | 6 | set project_name "PCS" 7 | 8 | set npaths 10 9 | 10 | set fname $project_name/sta_report.txt 11 | 12 | source utils.tcl 13 | 14 | setup_timing $project_name 15 | 16 | check_timing -file $fname 17 | 18 | report_timing -npaths $npaths -nworst 100 -show_routing -append -file $fname 19 | 20 | #report_timing -nworst $npaths -delay_type max -sort_by group -file $fname 21 | 22 | # delete timing netlist 23 | delete_timing_netlist 24 | 25 | # Close project 26 | project_close 27 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO list 2 | 3 | - add formal assertions 4 | 5 | - 10BASE-R 6 | - add testbench for full pcs tx lite in 16b 7 | 8 | - TX 9 | - test 10G version 10 | 11 | - RX 12 | - write 10G version 13 | - test 10G version 14 | - 40G CDC 15 | - fix broken geabox tb 16 | 17 | - Code clean : 18 | - rename `IS_40G` parameter to `IS_10G` 19 | 20 | - TCL: 21 | - singal tap file when I have fpga and probe 22 | 23 | - TB : 24 | - add 10g pcs tb 25 | - quartus simulation tb 26 | 27 | - FPGA, when reunited with build server :heart: 28 | - :warning: add cdc on nreset for 40G RX PCS -> gearbox 29 | -------------------------------------------------------------------------------- /tb/vpi/pcs_enc.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef PCS_ENC_H 8 | #define PCS_ENC_H 9 | #include "pcs_defs.h" 10 | /* PCS encoder functions */ 11 | 12 | /* Encode block control signals into the data and define sync header's value */ 13 | uint8_t enc_block( ctrl_lite_s ctrl, uint64_t data, block_s *block_enc ); 14 | uint8_t dec_block( uint8_t *ctrl, uint64_t *data, block_s block_dec ); 15 | 16 | #endif // PCS_ENC_H 17 | 18 | -------------------------------------------------------------------------------- /tv/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define I1 57 6 | #define I0 38 7 | 8 | int main(){ 9 | uint64_t reg = UINT64_MAX; 10 | uint64_t test = 0x1e; 11 | uint64_t res = 0; 12 | uint64_t t = 0; 13 | uint64_t i57, i38, ti0; 14 | printf("in: %016lx\nstate: %016lx\n", test, reg); 15 | for( int i=0; i<64; i++){ 16 | i57 = ( reg >> I1 ) & 0x01; 17 | i38 = ( reg >> I0 ) & 0x01; 18 | ti0 = ( test >> i) & 0x01; 19 | t = ( i57 ^ i38 ) ^ ti0 ; 20 | res |= t << i; 21 | reg = (reg << 1)| t; 22 | printf("[%02d] %d ; d %d i0 %d i1 %d\n", i, t, ti0, i38 , i57); 23 | } 24 | printf("output:%016lx\n", res); 25 | } 26 | -------------------------------------------------------------------------------- /tb/vpi/pcs_gearbox.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef PCS_GEARBOX_H 9 | #define PCS_GEARBOX_H 10 | #include "pcs_defs.h" 11 | 12 | /* PCS gearbox functions */ 13 | 14 | /* Produce the next 4x16b pma data, return 1 when gearbox is full */ 15 | uint8_t gearbox( gearbox_s * state, block_s block, uint64_t *pma ); 16 | 17 | static inline bool gearbox_full(gearbox_s state){ 18 | return state.len == 64; 19 | }; 20 | #endif // PCS_GEARBOX_H 21 | 22 | -------------------------------------------------------------------------------- /fake_reset.v: -------------------------------------------------------------------------------- 1 | /* fake reset module 2 | * Used to force reset signal for FPGA testbenches */ 3 | module fake_reset#( 4 | /* trigger reset when we detect and overflow on counter */ 5 | parameter CNT_W = 30 6 | )( 7 | input clk, 8 | input fpga_reset_i, 9 | 10 | output fake_reset_o 11 | ); 12 | logic of_cnt_add; 13 | logic [CNT_W-1:0] cnt_add; 14 | logic [CNT_W-1:0] cnt_next; 15 | reg [CNT_W-1:0] cnt_q; 16 | 17 | assign { of_cnt_add, cnt_add } = cnt_q + {{CNT_W-1{1'b0}}, 1'b1}; 18 | 19 | always @(posedge clk) begin 20 | if (fpga_reset_i) begin 21 | cnt_q <= {CNT_W{1'b0}}; 22 | end else begin 23 | cnt_q <= cnt_next; 24 | end 25 | end 26 | assign fake_reset_o = of_cnt_add; 27 | endmodule 28 | -------------------------------------------------------------------------------- /tb/vpi/vpi_v/tb_marker.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_MARKER_HPP 9 | #define TB_MARKER_HPP 10 | 11 | #define MODULE am_tx_tb 12 | 13 | #define STR_(x) #x 14 | #define STR(x) STR_(x) 15 | 16 | 17 | #define CAT2_(a,b) a##b 18 | #define CAT2(a,b) CAT2_(a,b) 19 | 20 | #define CAT3_(a,b,c) a##b##c 21 | #define CAT3(a,b,c) CAT3_(a,b,c) 22 | 23 | #define INC_STR STR(CAT2(V,MODULE).h) 24 | #define VMODULE CAT2(V,MODULE) 25 | #define MODULE_SIG_STR(x) STR(CAT3(TOP.,MODULE,.)x) 26 | 27 | #include INC_STR 28 | 29 | int main(int argc, char ** argv); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /tb/vpi/vpi_v/tb_pcs.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_PCS 9 | #define TB_PCS 10 | 11 | #define MODULE pcs_tb 12 | 13 | #define STR_(x) #x 14 | #define STR(x) STR_(x) 15 | 16 | 17 | #define CAT2_(a,b) a##b 18 | #define CAT2(a,b) CAT2_(a,b) 19 | 20 | #define CAT3_(a,b,c) a##b##c 21 | #define CAT3(a,b,c) CAT3_(a,b,c) 22 | 23 | #define INC_STR STR(CAT2(V,MODULE).h) 24 | #define VMODULE CAT2(V,MODULE) 25 | #define MODULE_SIG_STR(x) STR(CAT3(TOP.,MODULE,.)x) 26 | #define MODURE_STR STR(MODULE) 27 | #include INC_STR 28 | 29 | int main(int argc, char** argv); 30 | 31 | #endif//TB_PCS 32 | -------------------------------------------------------------------------------- /tb/vpi/tb_marker_common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_MARKER_COMMON_H 9 | #define TB_MARKER_COMMON_H 10 | 11 | #ifdef VERILATOR 12 | #include "verilated_vpi.h" 13 | #else 14 | #include 15 | #endif 16 | 17 | /* Generate the next turples of { data, head } and 18 | * the associated expected output { data, head } alongside 19 | * the flag to indicate if the marker is written within this 20 | * cycle */ 21 | int tb_marker( 22 | vpiHandle h_head_i, 23 | vpiHandle h_data_i, 24 | vpiHandle h_marker_v, 25 | vpiHandle h_head_o, 26 | vpiHandle h_data_o 27 | ); 28 | #endif //TB_MARKER_COMMON_H 29 | -------------------------------------------------------------------------------- /tb/vpi/tb_rand.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_RAND_H 9 | #define TB_RAND_H 10 | 11 | #include 12 | #include 13 | 14 | #ifndef SEED 15 | #define SEED 10 16 | #endif 17 | 18 | #define LFSR(x) ((x >> 1) | (( ((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 5)) & 1u) << 15)) 19 | 20 | void tb_rand_init(uint16_t seed); 21 | 22 | uint16_t tb_rand_get_lfsr(); 23 | 24 | uint16_t tb_rand_get_packet_len(); 25 | 26 | uint16_t tb_rand_packet_idle_cntdown(); 27 | 28 | uint64_t tb_rand_uint64_t(); 29 | 30 | uint8_t tb_rand_uint8_t(); 31 | 32 | void tb_rand_fill_packet(uint8_t * p, size_t len); 33 | 34 | #endif//TB_RAND_H 35 | -------------------------------------------------------------------------------- /tb/vpi/64b66b.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "64b66b.h" 9 | #include "defs.h" 10 | 11 | uint64_t scramble(uint64_t *state, uint64_t data, size_t len) 12 | { 13 | uint64_t g, res; 14 | res = 0; 15 | // G_0 = 1 ^ X_38 ^ X_58 16 | info("scrambler state %016lx\n", *state); 17 | for(size_t i= 0; i < len; i++){ 18 | g = (( data >> i) & 0x01 ) ^ (( *state >> I0_64b66b) & 0x01 ) ^ (( *state >> I1_64b66b) & 0x01 ); 19 | *state = ( *state << 1 ) | ( g & 0x01 ); 20 | res = res | ( (g & 0x01) << i ); 21 | } 22 | return res; 23 | 24 | } 25 | 26 | uint64_t descramble(uint64_t *state, uint64_t data, size_t len) 27 | { 28 | // TODO 29 | return 0; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /tcl/quartus_init.tcl: -------------------------------------------------------------------------------- 1 | load_package ::quartus::project 2 | load_package ::quartus::flow 3 | 4 | source utils.tcl 5 | 6 | # Setup PCS project for quartus 7 | 8 | set project_name "PCS" 9 | 10 | set fpga_family "Cyclone 10 GX" 11 | 12 | set project_dir $project_name 13 | 14 | set fpga_device 10CX150YF780E5G 15 | 16 | 17 | # create new quartus project 18 | project_new $project_name -overwrite 19 | 20 | # set fpga 21 | set_global_assignment -name FAMILY $fpga_family 22 | set_global_assignment -name DEVICE $fpga_device 23 | 24 | # project configuration 25 | set_global_assignment -name OPTIMIZATION_MODE "BALANCED" 26 | set_global_assignment -name PROJECT_OUTPUT_DIRECTORY $project_dir 27 | 28 | set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2009 29 | set_global_assignment -name ADD_PASS_THROUGH_LOGIC_TO_INFERRED_RAMS ON 30 | set_global_assignment -name VERILOG_MACRO QUARTUS 31 | set_global_assignment -name VERILOG_MACRO SYNTHESIS 32 | 33 | #close 34 | project_close 35 | -------------------------------------------------------------------------------- /tb/vpi/test.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TEST_H 9 | #define TEST_H 10 | #include "tv.h" 11 | #include 12 | #include 13 | #include 14 | #include "defs.h" 15 | 16 | int main(){ 17 | tv_t *t; 18 | uint8_t *data; 19 | ctrl_lite_s ctrl; 20 | 21 | memset( &ctrl, 0, sizeof(ctrl_lite_s )); 22 | 23 | ctrl.ctrl_v = true; 24 | ctrl.idle_v = true; 25 | data = 0; 26 | 27 | t = tv_alloc(); 28 | 29 | data = (uint8_t*) malloc(sizeof(uint8_t) * TXD_W*LANE_N); 30 | for(int i=0; i < 2; i++){ 31 | if (!tv_txd_has_data(t)) tv_create_packet(t); 32 | info("Reading data\n"); 33 | tv_get_next_txd(t, &ctrl, data ); 34 | } 35 | 36 | printf("raw data 0x%016lX\n\n", (size_t) data); 37 | free(data); 38 | 39 | tv_free(t); 40 | return 0; 41 | } 42 | #endif // TEST_H 43 | -------------------------------------------------------------------------------- /tb/vpi/tv.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TV_H 9 | #define TV_H 10 | 11 | #include 12 | #include "pcs_tx.h" 13 | #include "tb_fifo.h" 14 | 15 | #define IDX_TO_LANE(x) ( x % LANE_N ) 16 | typedef struct{ 17 | // tx channel 18 | pcs_tx_s *tx; 19 | // fifo 20 | tv_data_fifo_t *block[LANE_N]; 21 | tv_data_fifo_t *data[LANE_N]; 22 | // next packet to send 23 | size_t len; 24 | //size_t rd_idx; 25 | //size_t lane_idx; 26 | size_t debug_id; 27 | // idle count down 28 | size_t idle_cntdown; 29 | }tv_t; 30 | 31 | tv_t * tv_alloc(); 32 | 33 | void tv_create_packet(tv_t * t, const int start_lane); 34 | 35 | bool tv_get_next_txd( 36 | tv_t* t, 37 | ctrl_lite_s **ctrl, 38 | block_s **data, 39 | uint64_t *debug_id, 40 | const int lane); 41 | 42 | //bool tv_txd_has_data(tv_t* t); 43 | 44 | void tv_free(tv_t * t); 45 | 46 | #endif // TV_H 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /tcl/timing.sdc: -------------------------------------------------------------------------------- 1 | # Timing and clock information 2 | 3 | # would like to put 8 decimal places but 3 is the maximum 4 | set_time_format -unit ns -decimal_places 3 5 | 6 | 7 | # External clock sources 8 | 9 | # 50 MHz oscillator 10 | create_clock -period 20 [get_ports OSC_50m] 11 | 12 | # 644,53125 MHz oscillator on transiver bank 1D 13 | create_clock -period 1.551 [get_ports GXB1D_644M] 14 | 15 | # 125 MHz oscillator on transiver bank 1D 16 | create_clock -period 8.0 [get_ports GXB1D_125M] 17 | 18 | 19 | # only applies to 10G, 40G has an internal cdc 20 | set m_loop m_sfp*_pcs|m_pcs_loopback 21 | 22 | # nreset 2ff sync 23 | set_false_path -from [get_registers *sfp*_pcs|gx_nreset_q] -to [get_registers *sfp*|nreset_next] 24 | 25 | # User contrained generate clocks : for PLLs 26 | # ATX -> tx transiver 27 | derive_pll_clocks 28 | 29 | # rx -> tx, data and reset, both clk domains are running at same 30 | # frequency but different phase 31 | # TODO : look for a more precise rule 32 | set_false_path -from [get_registers $m_loop|pcs_rx*] \ 33 | -to [get_registers $m_loop|pcs_tx*] 34 | 35 | # User contrained clock uncertainty 36 | derive_clock_uncertainty 37 | -------------------------------------------------------------------------------- /tcl/utils.tcl: -------------------------------------------------------------------------------- 1 | # set global assigmenent wrapper 2 | proc sga_wrap {var val} { 3 | set ret [set_global_assignment -name $var $val] 4 | if {$ret != 0} { 5 | puts "Error: assigning $var to $val, code $ret" 6 | } 7 | } 8 | 9 | proc readports { regex } { 10 | set ports [get_ports $regex] 11 | foreach_in_collection port $ports { 12 | puts [get_port_info -name $port] 13 | } 14 | } 15 | 16 | proc readregs { regex } { 17 | set regs [get_registers $regex ] 18 | foreach_in_collection reg $regs { 19 | puts [get_object_info -name $reg] 20 | } 21 | } 22 | 23 | proc readclocks { regex } { 24 | set regs [get_clocks $regex ] 25 | foreach_in_collection reg $regs { 26 | puts [get_clock_info -name $reg] 27 | } 28 | } 29 | 30 | proc readnodes { regex } { 31 | set nodes [get_nodes $regex] 32 | foreach_in_collection node $nodes { 33 | puts [get_object_info -name $node] 34 | } 35 | } 36 | 37 | 38 | proc readclocks { regex } { 39 | set clocks [get_clocks $regex] 40 | foreach_in_collection clk $clocks { 41 | puts [get_clock_info -name $clk] 42 | } 43 | } 44 | 45 | 46 | proc setup_timing { project_name } { 47 | # Open project 48 | project_open $project_name 49 | 50 | # create timing netlist 51 | create_timing_netlist 52 | 53 | # target SDC file will have been configured at setup 54 | read_sdc 55 | 56 | update_timing_netlist 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tb/vpi/pcs_marker.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef PCS_MARKER_H 9 | #define PCS_MARKER_H 10 | #include "pcs_defs.h" 11 | 12 | /* Alignement marker specific code, this 13 | * file should only be compiled if we are 14 | * targetting a physical layer on 40GBASE 15 | * or 100GBASE */ 16 | #ifndef _40GBASE 17 | #ifndef _100GBASE 18 | #error "Error in defines, alignement marker is a 40G and upper feature" 19 | #endif 20 | #endif 21 | uint8_t calculate_bip_per_lane( 22 | uint8_t bip, 23 | const block_s out); 24 | 25 | void _create_alignement_marker( 26 | const size_t lane, 27 | const uint8_t bip3, 28 | block_s *out); 29 | 30 | // Add alignement marker to data block, return true if an alignement 31 | // marker was added to the block in place of the datad, false if not. 32 | // The block's content will be replaced with an alignement marker 33 | // once ever 16383 blocks. 34 | bool alignement_marker( 35 | marker_s *state, 36 | const size_t lane, 37 | const block_s in, 38 | block_s *out); 39 | 40 | // are we going to add alignement marker 41 | static inline bool is_alignement_marker( 42 | const marker_s state) 43 | { 44 | return ( state.gap == MARKER_GAP_N ); 45 | } 46 | #endif//PCS_MARKER_H 47 | -------------------------------------------------------------------------------- /tb/vpi/pcs_gearbox.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "pcs_gearbox.h" 9 | #include "defs.h" 10 | #include 11 | uint8_t gearbox( gearbox_s * state, block_s block, uint64_t *pma ){ 12 | if ( state->len >= 64 ){ 13 | // buffer is full : purge 14 | info("Gearbox purge, len %ld buff[0] %lx\n", state->len, state->buff[0]); 15 | memcpy( pma, &state->buff[0], sizeof(uint64_t)); 16 | state->len = 0; 17 | state->buff[0] = 0; 18 | state->buff[1] = 0; 19 | }else{ 20 | // buffer is not full : add 21 | info("gb start : [x%x]{%016lx, %016lx} len %ld\n", block.head, state->buff[1], state->buff[0], state->len); 22 | info("data %lx\n", block.data); 23 | uint64_t flop = state->buff[0]; 24 | state->buff[0] = block.data; 25 | state->buff[0]<<= 2; 26 | state->buff[0] |= block.head & 0x3; 27 | state->buff[0]<<=state->len; 28 | state->buff[0] |= flop; 29 | state->buff[1] = block.data >> ( 64 - ( state->len + 2)); 30 | info("{ buff[1] buff[0] } = { %lx, %lx} \n", state->buff[1], state->buff[0]); 31 | memcpy( pma, &state->buff[0], sizeof(uint64_t)); 32 | state->buff[0] = state->buff[1]; 33 | state->len += 2; 34 | if ( state->len >= 64 ) return 1; 35 | } 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tb/vpi/tb_rand.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "tb_rand.h" 9 | 10 | #include 11 | #include "tb_config.h" 12 | #include "defs.h" 13 | #include 14 | 15 | static bool lfsr_init = false; 16 | static uint16_t lfsr; 17 | 18 | #define LFSR_INIT if(lfsr_init == false)tb_rand_init(SEED) 19 | 20 | void tb_rand_init(uint16_t seed){ 21 | lfsr = seed; 22 | lfsr_init = true; 23 | } 24 | 25 | uint16_t tb_rand_get_lfsr(){ 26 | return lfsr; 27 | }; 28 | 29 | uint16_t tb_rand_get_packet_len(){ 30 | LFSR_INIT; 31 | lfsr = LFSR(lfsr); 32 | uint16_t rand_cnt = (uint16_t) ( lfsr % (PACKET_LEN_MAX-PACKET_LEN_MIN)) + PACKET_LEN_MIN; 33 | return rand_cnt; 34 | } 35 | 36 | 37 | uint16_t tb_rand_packet_idle_cntdown(){ 38 | LFSR_INIT; 39 | lfsr = LFSR(lfsr); 40 | return ( lfsr % (PACKET_IDLE_CNT_MAX-PACKET_IDLE_CNT_MIN )) + PACKET_IDLE_CNT_MIN; 41 | } 42 | 43 | uint64_t tb_rand_uint64_t(){ 44 | uint64_t r = 0; 45 | LFSR_INIT; 46 | for( int i=0; i<4; i++){ 47 | lfsr = LFSR(lfsr); 48 | r |= (uint64_t)lfsr << 16*i; 49 | } 50 | return r; 51 | } 52 | uint8_t tb_rand_uint8_t(){ 53 | LFSR_INIT; 54 | lfsr = LFSR(lfsr); 55 | return (uint8_t) lfsr; 56 | } 57 | void tb_rand_fill_packet(uint8_t * p, size_t len){ 58 | LFSR_INIT; 59 | for(size_t i=0; i < len; i++){ 60 | lfsr = LFSR(lfsr); 61 | p[i] = (uint8_t) lfsr; 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /xgmii_pcs_10g_enc.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* XGMII 10G PCS */ 9 | module xgmii_pcs_10g_tx #( 10 | parameter XGMII_DATA_W = 64, 11 | parameter LANE0_CNT_N = BLOCK_W/( 4 * 8), 12 | parameter LANE0_CNT_W = $clog2(LANE0_CNT_N)+1 13 | 14 | )( 15 | input clk, 16 | input nreset, 17 | 18 | input [XGMII_DATA_W-1:0] xgmii_txd_i, 19 | input [XGMII_CTRL_W-1:0] xgmii_txc_i, 20 | 21 | output ready_o, 22 | output [XGMII_DATA_W-1:0] data_o 23 | ); 24 | // xgmii interface to custom lite pcs interface 25 | logic ctrl_v; 26 | logic idle_v; 27 | logic term_v; 28 | logic err_v; 29 | logic [LANE0_CNT_W-1:0] start_v; 30 | logic [XGMII_KEEP_W-1:0] keep; // data keep 31 | 32 | xgmii_pcs_10g_enc_intf #( .XGMII_DATA_W(XGMII_DATA_W)) 33 | m_xgmii_intf( 34 | .clk(clk), 35 | .reset(reset), 36 | .xgmii_txd_i(xgmii_txd_i), 37 | .xgmii_txc_i(xgmii_txc_i), 38 | .ctrl_v_o(ctrl_v), 39 | .idle_v_o(idle_v), 40 | .start_v_o(start_v), 41 | .term_v_o(term_v), 42 | .err_v_o(err_v), 43 | .keep_o(keep) 44 | ); 45 | 46 | pcs_10g_tx #(.XGMII_DATA_W(XGMII_DATA_W)) 47 | m_pcs_tx( 48 | .clk(clk), 49 | .nreset(nreset), 50 | .ctrl_v_i(ctrl_v), 51 | .idle_v_i(idle_v), 52 | .start_i(start_v), 53 | .term_i(term_v), 54 | .err_i(err_v), 55 | .data_i(xgmii_txd_i), 56 | .keep_i(keep), 57 | .part_i(), // NC 58 | .keep_next_i(), //NC 59 | .ready_o(ready_o), 60 | .data_o(data_o) 61 | ); 62 | endmodule 63 | -------------------------------------------------------------------------------- /lane_reorder_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Reorder input block data to correct lane based on `lane_i` indicator. */ 9 | module lane_reorder_rx#( 10 | parameter LANE_N = 4, 11 | parameter BLOCK_W = 66 12 | )( 13 | `ifdef FORMAL 14 | input clk, 15 | `endif 16 | // lane identifier : onehot 17 | input [LANE_N*LANE_N-1:0] lane_i, 18 | // unordered block 19 | input [LANE_N*BLOCK_W-1:0] block_i, 20 | 21 | // reordered block 22 | output logic [LANE_N*BLOCK_W-1:0] block_o 23 | ); 24 | 25 | // unordered blocks 26 | logic [BLOCK_W-1:0] block[LANE_N-1:0]; 27 | // re-order results 28 | logic [BLOCK_W-1:0] res[LANE_N-1:0]; 29 | 30 | genvar x; 31 | generate 32 | for(x=0; x 14 | #endif 15 | 16 | /* Includes scanning */ 17 | void tb_vpi_put_logic_1b_t(vpiHandle h, uint8_t var); 18 | 19 | void tb_vpi_put_logic_uint8_t(vpiHandle h, uint8_t var); 20 | 21 | void tb_vpi_put_logic_uint32_t(vpiHandle h, uint32_t var); 22 | 23 | static inline void tb_vpi_put_logic_uint16_t(vpiHandle h, uint16_t var){ 24 | tb_vpi_put_logic_uint32_t(h, (uint32_t) var); 25 | }; 26 | 27 | void tb_vpi_put_logic_uint64_t(vpiHandle h, uint64_t var); 28 | 29 | 30 | // puts an array of char of variable length to a vector 31 | void _tb_vpi_put_logic_char_var_arr(vpiHandle h, uint8_t *arr, size_t len); 32 | 33 | // puts an array of uint64_t of variable length to a vector 34 | void tb_vpi_put_logic_uint64_t_var_arr(vpiHandle h, uint64_t *arr, size_t len); 35 | 36 | #define TB_UTILS_PUT_CHAR_ARR(X) \ 37 | static inline void tb_vpi_put_logic_char_##X##_t(vpiHandle h, uint8_t *arr){ \ 38 | _tb_vpi_put_logic_char_var_arr( h, arr, X ); \ 39 | } 40 | static inline void tb_vpi_put_logic_char(vpiHandle argc, char var){ 41 | tb_vpi_put_logic_uint8_t(argc, (uint8_t) var); 42 | } 43 | TB_UTILS_PUT_CHAR_ARR(2) 44 | TB_UTILS_PUT_CHAR_ARR(4) 45 | TB_UTILS_PUT_CHAR_ARR(8) 46 | TB_UTILS_PUT_CHAR_ARR(10) 47 | TB_UTILS_PUT_CHAR_ARR(20) 48 | 49 | // debug id 50 | TB_UTILS_PUT_CHAR_ARR(18) 51 | 52 | 53 | #endif // TB_UTILS_H 54 | -------------------------------------------------------------------------------- /tb/vpi/tb_pcs_common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_PCS_COMMON 9 | #define TB_PCS_COMMON 10 | 11 | #ifdef VERILATOR 12 | #include "verilated_vpi.h" 13 | #else 14 | #include 15 | #endif 16 | 17 | #include "tv.h" 18 | 19 | /* Get values of lane */ 20 | void tb_pcs_get_tx_lane( 21 | tv_t *tv_s, 22 | int lane, 23 | ctrl_lite_s **ctrl, 24 | block_s **data, 25 | uint64_t *debug_id 26 | ); 27 | /* write data to design */ 28 | void tb_pcs_set_data( 29 | ctrl_lite_s *ctrl[LANE_N], 30 | block_s *data[LANE_N], 31 | uint64_t debug_id[LANE_N], 32 | vpiHandle h_ready_o, 33 | vpiHandle h_ctrl_v_i, 34 | vpiHandle h_idle_v_i, 35 | vpiHandle h_start_v_i, 36 | vpiHandle h_term_v_i, 37 | vpiHandle h_term_keep_i, 38 | vpiHandle h_err_i, 39 | vpiHandle h_data_i, 40 | vpiHandle h_debug_id_i 41 | ); 42 | 43 | void tb_pcs_tx( 44 | tv_t *tv_s, 45 | vpiHandle h_ready_o, 46 | vpiHandle h_ctrl_v_i, 47 | vpiHandle h_idle_v_i, 48 | vpiHandle h_start_v_i, 49 | vpiHandle h_term_v_i, 50 | vpiHandle h_term_keep_i, 51 | vpiHandle h_err_i, 52 | vpiHandle h_data_i, 53 | vpiHandle h_debug_id_i 54 | ); 55 | 56 | void tb_pcs_get_exp_lane( 57 | tv_t *tv_s, 58 | int lane, 59 | block_s **block, 60 | uint64_t *debug_id 61 | ); 62 | 63 | void tb_pcs_set_exp_data( 64 | block_s *block[LANE_N], 65 | uint64_t debug_id[LANE_N], 66 | vpiHandle h_block_o, 67 | vpiHandle h_debug_id_o 68 | ); 69 | 70 | void tb_pcs_tx_exp( 71 | tv_t *tv_s, 72 | vpiHandle h_pma_o, 73 | vpiHandle h_debug_id_o 74 | ); 75 | 76 | #endif //TB_PCS_COMMON 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RTL Ethernet Physical Layer 2 | 3 | Parametrisable implementation of the Ethernet physical layer's PCS for **10GBASE-R** and **40GBASE-R**. 4 | 5 | RTL is platform agnostic but current synthesis flow target's the Intel **Cyclone 10 GX** FPGA series. 6 | 7 | ## Quickstart 8 | 9 | Pre-requisite : 10 | Have `quartus` installed, and set in `PATH`. 11 | 12 | This quickstart will create test project for the `10CX150YF780E5G` part with a 13 | `10GBASE-R` PCS and `40GBASE-R` PCS in loop-back mode on transceiver bank `1D`. 14 | 15 | ![Basic shematics of loopback!](/doc/quickstart.svg) 16 | 17 | To create a new quartus project, and run lint, synthesize, and check timing and 18 | then assemble; in the `tcl` directory run : 19 | ```sh 20 | make build 21 | ``` 22 | 23 | A project named `PCS.qsf` will be created in the `tcl` directory and all logs, including timing 24 | will be in the `tcl/PCS` directory. 25 | 26 | ### Quartus versions other then 22.3 27 | 28 | This project was build for `quartus 22,3`, compatibility issues with the intel `IP` might 29 | arise with other versions, to force compatibility use the `compat` argument. 30 | 31 | ```sh 32 | make build compat=1 33 | ``` 34 | 35 | ## Roadmap 36 | 37 | 10GBASE-R: 38 | 39 | - [ ] 16b wide data path, very low latency 40 | 41 | - [x] 64b wide data path, low latency requirement 42 | 43 | 40GBASE-R: 44 | 45 | - [x] 4 lanes, 256b wide data path, no latency requirement 46 | 47 | ## Features not supported 48 | 49 | - MDIO 50 | 51 | - RS - Reconciliation Sublayer 52 | 53 | - WIS - WAN Interface Sublayer 54 | 55 | - EEE - Ethernet Energy Efficiency 56 | 57 | ## Testing 58 | 59 | For more information on the testbenches and how to run them see the [README in the tb director](tb/README.md). 60 | 61 | # License 62 | 63 | This code uses the MIT license, all rights belong to Julia Desmazes. 64 | 65 | -------------------------------------------------------------------------------- /tb/vpi/vpi_i/tb_marker.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Iverilog specific vpi wrapping */ 9 | 10 | #include "tb_marker.h" 11 | #include "../tb_marker_common.h" 12 | #include 13 | #include "../defs.h" 14 | 15 | static int tb_marker_compiletf(char*user_data) 16 | { 17 | return 0; 18 | } 19 | 20 | // Drive PCS marker aligment input values 21 | static int tb_marker_calltf(char*user_data) 22 | { 23 | // get vpi argument handler 24 | vpiHandle sys; 25 | vpiHandle argv; 26 | 27 | sys = vpi_handle(vpiSysTfCall, 0); 28 | assert(sys); 29 | argv = vpi_iterate(vpiArgument, sys); 30 | assert(argv); 31 | 32 | // scan argv for parameters in the order they 33 | // appear in the function call 34 | vpiHandle h_head_i; 35 | vpiHandle h_data_i; 36 | vpiHandle h_marker_v; 37 | vpiHandle h_head_o; 38 | vpiHandle h_data_o; 39 | 40 | h_head_i = vpi_scan(argv); 41 | h_data_i = vpi_scan(argv); 42 | h_marker_v = vpi_scan(argv); 43 | h_head_o = vpi_scan(argv); 44 | h_data_o = vpi_scan(argv); 45 | 46 | // call common vpi section 47 | tb_marker(h_head_i,h_data_i,h_marker_v, 48 | h_head_o,h_data_o); 49 | 50 | return 0; 51 | } 52 | 53 | /* Async call through verilator vpi isn't possible, only 54 | * for iverilog as of writting */ 55 | void tb_marker_register() 56 | { 57 | s_vpi_systf_data tf_data; 58 | 59 | tf_data.type = vpiSysTask; 60 | tf_data.sysfunctype = 0; 61 | tf_data.tfname = "$tb_marker"; 62 | tf_data.calltf = tb_marker_calltf; 63 | tf_data.compiletf = tb_marker_compiletf; 64 | tf_data.sizetf = 0; 65 | tf_data.user_data = 0; 66 | vpi_register_systf(&tf_data); 67 | } 68 | 69 | void (*vlog_startup_routines[])() = { 70 | tb_marker_register, 71 | 0 72 | }; 73 | 74 | 75 | -------------------------------------------------------------------------------- /_64b66b_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* 64b66b descrambler used for the rx path. 9 | * 10 | * data(x) = sdata(x) ^ sdata_lsr(38) ^ sdata_lsr(57) */ 11 | module _64b66b_rx #( 12 | // descrabler input data 13 | parameter LEN = 264 14 | )( 15 | input nreset, 16 | input clk, 17 | 18 | input valid_i, 19 | input [LEN-1:0] scram_i, 20 | output [LEN-1:0] data_o 21 | ); 22 | localparam I0 = 38; 23 | localparam I1 = 57; 24 | localparam S_W = I1+1; 25 | // save last bit of previously inputed scrambled data 26 | reg [S_W-1:0] s_q; 27 | logic [S_W-1:0] s_next; 28 | 29 | // unscrable 30 | genvar i; 31 | generate 32 | for ( i = 0; i < LEN; i++ ) begin : xor_loop 33 | if ( i <= I0 ) begin : gen_i_le_I0 34 | assign data_o[i] = scram_i[i] ^ (s_q[I0-i] ^ s_q[I1-i]); 35 | end else if ( i <= I1 ) begin : gen_i_le_I1 36 | assign data_o[i] = scram_i[i] ^ (scram_i[i-(I0+1)] ^ s_q[I1-i]); 37 | end else begin : gen_i_default 38 | assign data_o[i] = scram_i[i] ^ (scram_i[i-(I0+1)] ^ scram_i[i-(I1+1)]); 39 | end 40 | end 41 | 42 | for( i=0; i < S_W; i++) begin : gen_s_next_loop 43 | // flop input data to be used next cycle 44 | if ( i < LEN ) begin : gen_i_lt_len 45 | // input data is larger than internal state, rewite all bits 46 | assign s_next[i] = scram_i[LEN-1-i]; 47 | end else begin : gen_i_ge_len 48 | // input data is smaller than internal state, shift 49 | // state and rewrite only lower order bits 50 | assign s_next[i] = s_q[i-LEN]; 51 | end 52 | end 53 | 54 | endgenerate 55 | 56 | always @(posedge clk) begin 57 | if ( ~nreset ) begin 58 | // scrambler's initial start is set to all 1's 59 | s_q <= {S_W{1'b1}}; 60 | end else if(valid_i) begin 61 | s_q <= s_next; 62 | end 63 | end 64 | 65 | endmodule 66 | 67 | -------------------------------------------------------------------------------- /deskew_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Buffers must contain at least as much as the max dynamic skew. 9 | * The maxium skew is the largest difference in the fill level 10 | * of the buffers at any time. 11 | * Lane markers read out of each lane at the same time */ 12 | module deskew_rx #( 13 | parameter LANE_N = 4, 14 | parameter BLOCK_W = 66, 15 | /* max dynamic skew */ 16 | parameter MAX_SKEW_BIT_N = 1856, 17 | parameter MAX_SKEW_BLOCK_N = ( MAX_SKEW_BIT_N - BLOCK_W -1 )/BLOCK_W 18 | )( 19 | input clk, 20 | input nreset, 21 | 22 | /* Alignement marker lock */ 23 | /* we are seeing an alignement marker on lane */ 24 | input [LANE_N-1:0] am_lite_v_i, 25 | /* lite lock, we have seen an valid alignement marker on this lane */ 26 | input [LANE_N-1:0] am_lite_lock_v_i, 27 | 28 | // block data 29 | input [LANE_N*BLOCK_W-1:0] data_i, 30 | 31 | // deskewed alignement marker valid 32 | output am_v_o, 33 | // deskwed data 34 | output [LANE_N*BLOCK_W-1:0] data_o 35 | ); 36 | // identify slowest lane in order to find 37 | // the alignement marker 38 | logic [LANE_N-1:0] slow_lane; 39 | assign am_v_o = |(am_lite_v_i & slow_lane); 40 | 41 | logic am_lite_lock_full_v; 42 | assign am_lite_lock_full_v = &am_lite_lock_v_i; 43 | genvar l; 44 | generate 45 | for(l=0; l= S_W 59 | assign s_next[i] = res[LEN-1-i]; 60 | end 61 | end 62 | endgenerate 63 | 64 | always @(posedge clk) begin 65 | if ( ~nreset ) begin 66 | // scrambler's initial start is set to all 1's 67 | s_q <= {S_W{1'b1}}; 68 | end else if (valid_i) begin 69 | s_q <= s_next; 70 | end 71 | end 72 | 73 | // output 74 | assign scram_o = res; 75 | endmodule 76 | 77 | -------------------------------------------------------------------------------- /tb/am_tx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `ifndef TB_LOOP_N 9 | `define TB_LOOP_N 60000 10 | `endif 11 | 12 | /* marker_tb 13 | * TB to test the market alignement feature. 14 | * Written to help test this feature in isolation 15 | * using the same C model as the vpi in hopes to 16 | * hit this case faster. This marker will only be 17 | * added once every 2^14 cycles. 18 | */ 19 | module am_tx_tb; 20 | parameter LANE_N = 4; 21 | parameter HEAD_W = 2; 22 | parameter DATA_W = 64; 23 | 24 | reg clk = 1'b0; 25 | reg nreset; 26 | 27 | logic valid_i; 28 | 29 | logic [LANE_N*HEAD_W-1:0] head_i; 30 | logic [LANE_N*DATA_W-1:0] data_i; 31 | 32 | logic [LANE_N*HEAD_W-1:0] head_o; 33 | logic [LANE_N*DATA_W-1:0] data_o; 34 | 35 | logic [LANE_N*DATA_W-1:0] tb_data_o; 36 | logic [LANE_N*HEAD_W-1:0] tb_head_o; 37 | 38 | logic [LANE_N*DATA_W-1:0] tb_data_diff; 39 | 40 | logic marker_v_o; 41 | logic tb_marker_v_o; 42 | 43 | /*verilator lint_off BLKSEQ*/ 44 | always #5 clk = ~clk; 45 | /*verilator lint_on BLKSEQ*/ 46 | 47 | initial begin 48 | `ifndef VERILATOR 49 | $dumpfile("wave/am_tx_tb.vcd"); 50 | $dumpvars(0, am_tx_tb); 51 | `endif 52 | nreset = 1'b0; 53 | #10 54 | valid_i = 1'b1; 55 | `ifdef VERILATOR 56 | #1 57 | `endif 58 | // begin testing 59 | for(int i=0; i< `TB_LOOP_N; i++)begin 60 | #9 61 | nreset = 1'b1; 62 | `ifdef DEBUG 63 | $display("time %t", $time); 64 | `endif 65 | `ifndef VERILATOR 66 | $tb_marker(head_i,data_i, tb_marker_v_o, tb_head_o, tb_data_o); 67 | `endif 68 | #1 69 | check(); 70 | end 71 | $finish; 72 | end 73 | 74 | assign tb_data_diff = data_o ^ tb_data_o; 75 | 76 | task check(); 77 | assert(tb_marker_v_o == marker_v_o); 78 | assert(tb_head_o == head_o); 79 | assert(tb_data_o == data_o); 80 | endtask 81 | am_tx #(.LANE_N(LANE_N)) m_uut( 82 | .clk(clk), 83 | .nreset(nreset), 84 | .valid_i(valid_i), 85 | .head_i(head_i), 86 | .data_i(data_i), 87 | .marker_v_o(marker_v_o), 88 | .head_o(head_o), 89 | .data_o(data_o) 90 | ); 91 | 92 | 93 | endmodule 94 | -------------------------------------------------------------------------------- /tb/vpi/pcs_enc.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "pcs_enc.h" 9 | #include "defs.h" 10 | #include 11 | #include 12 | uint8_t enc_block( ctrl_lite_s ctrl, uint64_t data, block_s *block_enc ){ 13 | info("ENC : ctrl_v %x start_v[0] %x idle_v %x term_v %x term_keep %08x\n", 14 | ctrl.ctrl_v, 15 | ctrl.start_v[0], 16 | ctrl.idle_v, 17 | ctrl.term_v, 18 | ctrl.term_keep); 19 | if ( ctrl.ctrl_v ) { 20 | uint8_t type = 0; 21 | if ( ctrl.idle_v ){ 22 | type = BLOCK_TYPE_CTRL; 23 | data = BLOCK_CTRL_IDLE; 24 | } 25 | if ( ctrl.start_v[0] )type = BLOCK_TYPE_START_0; 26 | #ifndef _40GBASE 27 | if ( ctrl.start_v[1]) type = BLOCK_TYPE_START_4; 28 | #endif 29 | if ( ctrl.err_v ){ 30 | type = BLOCK_TYPE_CTRL; 31 | data = BLOCK_CTRL_IDLE; 32 | } 33 | if ( ctrl.term_v ){ 34 | info("term %x keep %x\n", ctrl.term_v, ctrl.term_keep); 35 | switch( ctrl.term_keep){ 36 | case 0x00 : type = BLOCK_TYPE_TERM_0; 37 | break; 38 | case 0x01 : type = BLOCK_TYPE_TERM_1; 39 | break; 40 | case 0x03 : type = BLOCK_TYPE_TERM_2; 41 | break; 42 | case 0x07 : type = BLOCK_TYPE_TERM_3; 43 | break; 44 | case 0x0F : type = BLOCK_TYPE_TERM_4; 45 | break; 46 | case 0x1F : type = BLOCK_TYPE_TERM_5; 47 | break; 48 | case 0x3F : type = BLOCK_TYPE_TERM_6; 49 | break; 50 | case 0x7F : type = BLOCK_TYPE_TERM_7; 51 | break; 52 | default : fprintf(stderr, "ENC: Unknown keep when setting term type, got %x\n", ctrl.term_keep); 53 | assert(0); 54 | return 1; 55 | } 56 | } 57 | if ( !type ) { 58 | fprintf(stderr, "ENC : Unidentified ctrl type\n"); 59 | assert(0); 60 | return 1; 61 | } 62 | block_enc->head = SYNC_HEAD_CTRL; 63 | block_enc->data = ( data & ~0xffULL) | ( type & 0xffULL); 64 | }else{ 65 | block_enc->head = SYNC_HEAD_DATA; 66 | block_enc->data = data; 67 | } 68 | return 0; 69 | }; 70 | 71 | uint8_t dec_block( uint8_t *ctrl, uint64_t *data, block_s block_dec ){ 72 | // TODO 73 | return 1; 74 | } 75 | -------------------------------------------------------------------------------- /tb/pcs_10g_loopback_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `ifndef TB_LOOP_N 9 | `define TB_LOOP_N 10 10 | `endif 11 | 12 | `define SYNC_CTRL 2'b10 13 | `define SYNC_DATA 2'b01 14 | 15 | `define SET_RANDOM_HEAD \ 16 | /*verilator lint_off WIDTHTRUNC */ \ 17 | head = $random % 2 ? `SYNC_DATA : `SYNC_CTRL; \ 18 | /*verilator lint_on WIDTHTRUNC */ 19 | 20 | /* pcs 10g loopback tb 21 | * Small test bench to check module behavior 22 | */ 23 | module pcs_10g_loopback_tb; 24 | parameter DATA_W = 64; 25 | parameter HEAD_W = 2; 26 | parameter BLOCK_W = HEAD_W + DATA_W; 27 | 28 | reg clk = 1'b0; 29 | reg nreset; 30 | 31 | logic rx_locked_i; 32 | logic [DATA_W-1:0] rx_par_data_i; 33 | 34 | /* verilator lint_off UNUSEDSIGNAL */ 35 | logic [DATA_W-1:0] tx_par_data_o; 36 | /* verilator lint_on UNUSEDSIGNAL */ 37 | 38 | /*verilator lint_off BLKSEQ*/ 39 | always #5 clk = ~clk; 40 | /*verilator lint_on BLKSEQ*/ 41 | 42 | 43 | task send_valid_block(); 44 | logic [BLOCK_W-1:0] buff; 45 | logic [HEAD_W-1:0] head; 46 | `SET_RANDOM_HEAD 47 | buff = { $random, $random, head }; 48 | 49 | rx_par_data_i = buff[DATA_W-1:0]; 50 | buff = buff >> DATA_W; 51 | for(int i=2; i < 64; i=i+2) begin 52 | #10 53 | rx_par_data_i = buff[DATA_W-1:0]; 54 | `SET_RANDOM_HEAD 55 | buff = { $random, $random, head }; 56 | /* verilator lint_off WIDTHTRUNC */ 57 | rx_par_data_i = buff << i; 58 | /* verilator lint_on WIDTHTRUNC */ 59 | buff = buff >> i; 60 | end 61 | #10 62 | rx_par_data_i = buff[DATA_W-1:0]; 63 | #10 64 | rx_par_data_i = {DATA_W{1'bx}}; 65 | endtask 66 | 67 | initial begin 68 | $dumpfile("wave/pcs_10g_loopback_tb.vcd"); 69 | $dumpvars(0, pcs_10g_loopback_tb); 70 | nreset = 1'b0; 71 | #10 72 | nreset = 1'b1; 73 | rx_locked_i = 1'b0; 74 | #20 75 | // begin testing 76 | for(int i=0; i< `TB_LOOP_N; i++)begin 77 | rx_locked_i = 1'b1; 78 | send_valid_block(); 79 | end 80 | #10 81 | $finish; 82 | end 83 | 84 | 85 | pcs_10g_loopback m_pcs_10g_loopback( 86 | .rx_par_clk (clk), 87 | .tx_par_clk (clk), 88 | .nreset (nreset), 89 | .rx_locked_i (rx_locked_i), 90 | .rx_par_data_i (rx_par_data_i), 91 | .tx_par_data_o (tx_par_data_o) 92 | ); 93 | 94 | endmodule 95 | -------------------------------------------------------------------------------- /tb/vpi/vpi_v/tb_marker.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #include "tb_marker.hpp" 8 | #include "verilated.h" 9 | #include "verilated_vpi.h" // Required to get definitions 10 | #include "../tb_marker_common.h" 11 | 12 | #if VM_TRACE 13 | # include // Trace file format header 14 | #endif 15 | 16 | uint64_t main_time = 0; // See comments in first example 17 | double sc_time_stamp() { return main_time; } 18 | 19 | 20 | int main(int argc, char** argv) { 21 | Verilated::commandArgs(argc, argv); 22 | 23 | 24 | const std::unique_ptr contextp{new VerilatedContext}; 25 | const std::unique_ptr top{new VMODULE{contextp.get()}}; 26 | 27 | #if VM_TRACE // makefile wave invoked with wave=1 28 | Verilated::traceEverOn(true);// computer trace signals 29 | VL_PRINTF("Enabling waves...\n"); 30 | VerilatedVcdC* tfp = new VerilatedVcdC; 31 | top->trace (tfp, 5);// trace 5 levels of hierachy 32 | tfp->open ("wave/"STR(MODULE)".vcd"); // Open the dump file 33 | #endif 34 | 35 | 36 | //contextp->internalsDump(); // See scopes to help debug 37 | 38 | // vpiHandlers 39 | vpiHandle h_head_i = vpi_handle_by_name((PLI_BYTE8*)"TOP.am_tx_tb.head_i", NULL); 40 | vpiHandle h_data_i = vpi_handle_by_name((PLI_BYTE8*)"TOP.am_tx_tb.data_i", NULL); 41 | vpiHandle h_marker_v_o = vpi_handle_by_name((PLI_BYTE8*)"TOP.am_tx_tb.tb_marker_v_o", NULL); 42 | vpiHandle h_head_o = vpi_handle_by_name((PLI_BYTE8*)"TOP.am_tx_tb.tb_head_o", NULL); 43 | vpiHandle h_data_o = vpi_handle_by_name((PLI_BYTE8*)"TOP.am_tx_tb.tb_data_o", NULL); 44 | 45 | /* reset sequence 46 | * Hold reset for 1 clk cycle -> 10 C cycles */ 47 | for(; main_time<18; main_time++){ 48 | top->eval(); 49 | #if VM_TRACE 50 | if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp 51 | #endif 52 | } 53 | while (!contextp->gotFinish()) { 54 | top->eval(); 55 | VerilatedVpi::callValueCbs(); // For signal callbacks 56 | if ( main_time % 10 == 0 ){ 57 | tb_marker(h_head_i,h_data_i,h_marker_v_o, 58 | h_head_o,h_data_o); 59 | } 60 | #if VM_TRACE 61 | if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp 62 | #endif 63 | 64 | main_time++; 65 | } 66 | 67 | 68 | #if VM_TRACE 69 | if (tfp) tfp->close(); 70 | #endif 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /tb/vpi/tb_marker_common.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "tb_marker_common.h" 9 | #include "pcs_defs.h" 10 | #include "pcs_marker.h" 11 | #include "tb_rand.h" 12 | #include 13 | #include "defs.h" 14 | #include "tb_utils.h" 15 | 16 | #ifndef LANE_N 17 | #define LANE_N 4 18 | #endif 19 | 20 | static marker_s state; 21 | 22 | uint8_t format_head(block_s b[LANE_N]){ 23 | uint8_t h=0; 24 | for(int i=0; i 18 | #include 19 | #include 20 | #include 21 | #include "defs.h" 22 | pcs_tx_s *pcs_tx_init(){ 23 | pcs_tx_s * s; 24 | // alloc 25 | s = (pcs_tx_s*) malloc( sizeof( pcs_tx_s)); 26 | // init 27 | memset( s, 0, sizeof( pcs_tx_s)); 28 | s->scrambler_state = UINT64_MAX; 29 | return s; 30 | } 31 | 32 | bool get_next_exp(pcs_tx_s *state, const ctrl_lite_s ctrl, block_s* b_data, block_s *block) 33 | { 34 | uint8_t err = 0; 35 | bool gb_full_next = true; 36 | bool accept; 37 | #ifdef _40GBASE 38 | const size_t l = state->lane_idx; 39 | #else 40 | const size_t l = 0; 41 | #endif 42 | uint64_t data = b_data->data; 43 | 44 | #ifdef _40GBASE 45 | accept = !is_alignement_marker(state->marker_state); 46 | #else 47 | accept = true; 48 | #endif 49 | 50 | 51 | info("[%ld] accept %d marker %d\n",l, accept, !accept); 52 | info("raw data %016lx\n", data); 53 | // if gearbox is full next block will not be accpted anyways 54 | // so there is no need to compute the expected result 55 | if ( accept ){ 56 | // enc 57 | err = enc_block(ctrl, data, &(state->block_enc)); 58 | info("ENC : head x%01x, data x%016lx\n", state->block_enc.head,state->block_enc.data); 59 | if ( err != 0 ){ 60 | fprintf(stderr, "Error in encoding\n"); 61 | assert(0); // die with coredump 62 | } 63 | //scramble 64 | //skip scrambler on gearbox pause 65 | state->block_scram.data = scramble(&state->scrambler_state, state->block_enc.data, 64); 66 | state->block_scram.head = state->block_enc.head; 67 | info("Scramm in x%016lx out x%016lx\n", state->block_enc.data, state->block_scram.data); 68 | } 69 | #ifdef _40GBASE 70 | // alignment marker 71 | alignement_marker(&state->marker_state, state->lane_idx, state->block_scram, &state->block_mark[l] ); 72 | info("Marker in x%016lx out x%016lx\n", state->block_scram.data,state->block_mark[l].data); 73 | 74 | // set output block data 75 | block->data = state->block_mark[l].data; 76 | block->head = state->block_mark[l].head; 77 | info("exp, mark { %016lx, %01x }\n", block->data, 0x3 & block->head ); 78 | state->lane_idx = (state->lane_idx+1) % LANE_N; 79 | 80 | #else 81 | 82 | block->data = state->block_scram.data; 83 | block->head = state->block_scram.head; 84 | info("exp, scram { %016lx, %01x }\n", block->data, 0x3 & block->head ); 85 | 86 | #endif // _40GBASE 87 | 88 | 89 | return accept; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /deskew_lane_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Per lane deskew module. 9 | * Buffers blocks to realign all lanes on the slowest. 10 | * In order to save on buffer space we are discarding 11 | * the alignement marker at this stage. */ 12 | module deskew_lane_rx #( 13 | parameter BLOCK_W = 66, 14 | /* max dynamic skew */ 15 | parameter MAX_SKEW_BIT_N = 1856, 16 | parameter MAX_SKEW_BLOCK_N = ( MAX_SKEW_BIT_N - BLOCK_W -1 )/BLOCK_W, 17 | parameter SKEW_CNT_W = $clog2(MAX_SKEW_BLOCK_N) 18 | )( 19 | input clk, 20 | input nreset, 21 | 22 | /* Alignement marker lock */ 23 | input am_lite_v_i, // alignement marker block valid this cycle 24 | //input am_lite_lock_lost_v_i, // am lock lost 25 | input am_lite_lock_full_v_i, // all lanes have seen there alignement marker 26 | input [BLOCK_W-1:0] data_i, 27 | 28 | // skew offset is zero, used to identify lattest lane 29 | output skew_zero_o, 30 | // deskewed lane data 31 | output [BLOCK_W-1:0] data_o 32 | ); 33 | // keep track of skew 34 | reg [SKEW_CNT_W-1:0] skew_q; 35 | logic [SKEW_CNT_W-1:0] skew_next; 36 | logic [SKEW_CNT_W-1:0] skew_add; 37 | logic unused_skew_add_of; 38 | logic skew_rst; 39 | logic skew_en; 40 | 41 | assign skew_rst = am_lite_v_i; 42 | 43 | assign { unused_skew_add_of, skew_add } = skew_q + { {SKEW_CNT_W-1{1'b0}}, 1'b1}; 44 | assign skew_next = skew_rst ? {SKEW_CNT_W{1'b0}} : skew_add; 45 | assign skew_en = ~am_lite_lock_full_v_i; 46 | always @(posedge clk) begin 47 | if ( ~nreset ) begin 48 | skew_q <= '0; 49 | end else if ( skew_en ) begin 50 | skew_q <= skew_next; 51 | end 52 | end 53 | 54 | // shift buffer 55 | logic [MAX_SKEW_BLOCK_N*BLOCK_W-1:0] buff_q; 56 | logic [MAX_SKEW_BLOCK_N*BLOCK_W-1:0] buff_next; 57 | 58 | assign buff_next[BLOCK_W-1:0] = data_i; 59 | 60 | genvar x; 61 | generate 62 | for(x=1; x < MAX_SKEW_BLOCK_N; x++) begin : gen_buff_next_loop 63 | assign buff_next[x*BLOCK_W+:BLOCK_W] = buff_q[(x-1)*BLOCK_W+:BLOCK_W]; 64 | end 65 | endgenerate 66 | 67 | always_ff @(posedge clk) begin 68 | buff_q <= buff_next; 69 | end 70 | 71 | // skew is used as read pointer 72 | logic [BLOCK_W-1:0] buff_rd; 73 | always_comb begin 74 | /* default */ 75 | buff_rd = {BLOCK_W{1'bx}}; 76 | 77 | for(int j=0; j 8 | #include 9 | #include 10 | #include 11 | 12 | #include "tb_fifo.h" 13 | /* 14 | * Construct and return an pma fifo. 15 | */ 16 | tv_data_fifo_t *tb_data_fifo_ctor( 17 | void 18 | ) 19 | { 20 | tv_data_fifo_t *fifo = malloc_(tv_data_fifo_t); 21 | slisth_init(&fifo->elems); 22 | return fifo; 23 | } 24 | 25 | /* 26 | * Delete @fifo. 27 | */ 28 | void tb_data_fifo_dtor( 29 | tv_data_fifo_t *fifo 30 | ) 31 | { 32 | assert(fifo); 33 | slist *nod = 0; 34 | while((nod = slisth_pull(&fifo->elems))) { 35 | assert(nod); 36 | tv_data_fifo_elem_t *elem = cntof(nod, tv_data_fifo_elem_t, elems); 37 | free(elem->data); 38 | free(elem->ctrl); 39 | free(elem); 40 | } 41 | free(fifo); 42 | } 43 | 44 | /* 45 | * Construct and push an element in @fifo. 46 | */ 47 | void tb_data_fifo_push( 48 | tv_data_fifo_t *fifo, 49 | block_s *nv, 50 | ctrl_lite_s *ctrl, 51 | uint64_t debug_id 52 | ) 53 | { 54 | assert(fifo); 55 | tv_data_fifo_elem_t *elem = malloc_(tv_data_fifo_elem_t); 56 | elem->data = nv; 57 | elem->ctrl=ctrl; 58 | elem->debug_id = debug_id; 59 | slisth_push(&fifo->elems, &elem->elems); 60 | #if 0 61 | //#ifdef DEBUG 62 | printf("fifo push :\n"); 63 | tb_data_print_fifo(fifo); 64 | #endif 65 | } 66 | 67 | /* 68 | * If @fifo is not empty, pop an element and return it. 69 | * Otherwise, return 0. 70 | */ 71 | block_s *tb_data_fifo_pop( 72 | tv_data_fifo_t *fifo, 73 | uint64_t *debug_id, 74 | ctrl_lite_s **ctrl 75 | ) 76 | { 77 | 78 | /* Pop. */ 79 | assert(fifo); 80 | assert(debug_id); 81 | slist *nod = slisth_pull(&fifo->elems); 82 | if (!nod) return NULL; 83 | tv_data_fifo_elem_t *pop = cntof(nod, tv_data_fifo_elem_t, elems); 84 | assert(!pop->elems.next); 85 | 86 | /* Read data, delete @pop. */ 87 | *debug_id = pop->debug_id; 88 | *ctrl = pop->ctrl; 89 | block_s *ret = pop->data; 90 | free(pop); 91 | #ifdef DEBUG 92 | printf("fifo pop, debug id : 0x"); 93 | printf("id %016lx\n",*debug_id); 94 | printf("fifo poped structure :\n"); 95 | //tb_data_print_elem(ret); 96 | // print fifo 97 | tb_data_print_fifo(fifo); 98 | #endif 99 | 100 | /* Complete. */ 101 | return ret; 102 | 103 | } 104 | 105 | /* 106 | * Print a descriptor for all elements of @fifo. 107 | */ 108 | void tb_data_print_fifo( 109 | tv_data_fifo_t *fifo 110 | ) 111 | { 112 | if (!fifo) return; 113 | printf("Fifo :\n"); 114 | slist *nod = fifo->elems.read; 115 | while (nod) { 116 | tv_data_fifo_elem_t *elem = cntof(nod, tv_data_fifo_elem_t, elems); 117 | tb_data_print_elem(elem); 118 | nod = nod->next; 119 | } 120 | printf("\n"); 121 | } 122 | 123 | /* 124 | * Print the content of a fifo element @elem 125 | */ 126 | void tb_data_print_elem( 127 | tv_data_fifo_elem_t *elem 128 | ){ 129 | printf("id %016lx data {%016lx, %01x}\n", 130 | elem->debug_id, 131 | elem->data->data, 132 | elem->data->head); 133 | } 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /tb/_64b66b_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `ifndef TB_LOOP_N 9 | `define TB_LOOP_N 100 10 | `endif 11 | 12 | module _64b66b_tb; 13 | localparam LEN = 40; 14 | 15 | /* known test vector, used in test 1 */ 16 | localparam TV_W = 64; 17 | 18 | reg clk = 1'b0; 19 | reg nreset; 20 | logic valid_i; 21 | logic [LEN-1:0] data_i; 22 | logic [LEN-1:0] data_o; 23 | logic [LEN-1:0] scram_o; 24 | 25 | 26 | /* debug signals 27 | * db_data_diff : check binary differences between 28 | * original input to scrambler - descrambler pair 29 | * and final output. 30 | * We expect there to be no difference */ 31 | logic [LEN-1:0] db_data_diff; 32 | 33 | /*verilator lint_off BLKSEQ*/ 34 | always #5 clk = ~clk; 35 | /*verilator lint_on BLKSEQ*/ 36 | 37 | logic [TV_W-1:0] tb_data = 64'h1e ; 38 | // expected output can be generated using the program 39 | // located in `tb` 40 | logic [TV_W-1:0] tb_scram = 64'h7bfff0800000001e; 41 | 42 | // generate a random sequence and pass it though 43 | // the scrambler and the de-scrambler. 44 | // We expect the output to ultimatly 45 | // match the original data 46 | task test_sramble_decramble(int loop_cnt); 47 | logic [LEN-1:0] test; 48 | 49 | for( int i = 0; i < loop_cnt; i++) begin 50 | test = set_test(); 51 | data_i = test; 52 | #1 53 | assert(~|db_data_diff); 54 | #9 55 | /* randomly turn off valid */ 56 | valid_i = $random; 57 | end 58 | endtask 59 | 60 | function logic [LEN-1:0] set_test(); 61 | logic [LEN-1:0] tmp; 62 | if ( LEN <= 32 ) begin 63 | tmp = $random; 64 | end else begin 65 | for( int i=0; i < (LEN + LEN-1)/32; i++) begin 66 | tmp = ( tmp << 32 | $random ); 67 | end 68 | end 69 | return tmp; 70 | endfunction 71 | 72 | assign db_data_diff = data_i ^ data_o; 73 | 74 | initial begin 75 | $dumpfile("wave/_64b66b_tb.vcd"); 76 | $dumpvars(0, _64b66b_tb); 77 | nreset = 1'b0; 78 | #10 79 | nreset = 1'b1; 80 | // begin testing 81 | // test 1 : simple know correct test vector 82 | if ( LEN == 32 ) begin 83 | $display("test 1 %t", $time); 84 | valid_i = 1'b1; 85 | data_i = tb_data[31:0]; 86 | #1 87 | assert(tb_scram[31:0] == scram_o[31:0]); 88 | #9 89 | data_i = tb_data[63:32]; 90 | #1 91 | assert(tb_scram[63:32] == scram_o[31:0]); 92 | #9 93 | $display("test 1 : PASS"); 94 | end else begin 95 | $display("test 1 : SKIP, LEN parameter not 32"); 96 | end 97 | // test 2 : verify the output of the descambler 98 | // matches initial data 99 | $display("test 2 %t", $time); 100 | test_sramble_decramble(`TB_LOOP_N); 101 | 102 | #10 103 | $display("Test finished\n"); 104 | $finish; 105 | end 106 | 107 | _64b66b_tx #( .LEN(LEN)) 108 | m_64b66b_tx( 109 | .clk(clk), 110 | .nreset(nreset), 111 | .valid_i(valid_i), 112 | .data_i(data_i), 113 | .scram_o(scram_o) 114 | ); 115 | 116 | 117 | _64b66b_rx #( .LEN(LEN)) 118 | m_66b64b_rx( 119 | .clk(clk), 120 | .nreset(nreset), 121 | .valid_i(valid_i), 122 | .scram_i(scram_o), 123 | .data_o(data_o) 124 | ); 125 | endmodule 126 | -------------------------------------------------------------------------------- /tb/lane_reorder_rx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `define TB_LOOP_CNT 10 9 | 10 | module lane_reorder_rx_tb; 11 | 12 | localparam LANE_N = 4; 13 | localparam LANE_W = $clog2(LANE_N); 14 | localparam BLOCK_W = 66; 15 | 16 | logic [LANE_N*LANE_N-1:0] lane_i; 17 | logic [LANE_N*BLOCK_W-1:0] block_i; 18 | logic [LANE_N*BLOCK_W-1:0] block_o; 19 | 20 | logic [LANE_N-1:0] tb_lane_i [LANE_N-1:0]; 21 | logic [BLOCK_W-1:0] tb_block_i [LANE_N-1:0]; 22 | logic [BLOCK_W-1:0] tb_block_o [LANE_N-1:0]; 23 | 24 | 25 | // check if we get the correct data 26 | // in output block 27 | task check_correct_data(); 28 | /* verilator lint_off UNUSEDSIGNAL*/ 29 | int idx; 30 | /* verilator lint_on UNUSEDSIGNAL*/ 31 | for( int i= 0; i < LANE_N; i++ ) begin: check_data_idx_loop 32 | /* lane index is encoded on onehot 33 | * get index of first 1 in onehot vector */ 34 | idx =$clog2( tb_lane_i[i]); 35 | `ifdef DEBUG 36 | $display("check reorder data match for lane %d", idx); 37 | `endif 38 | assert( tb_block_i[i] == tb_block_o[idx]); 39 | end 40 | endtask 41 | 42 | // Assign each block a unique re-order id and 43 | // check if we get the correct data 44 | task test_reorder(); 45 | // each block needs a unique lane id 46 | // we are going to assign them sequentially 47 | // to make sure this constraint is respected 48 | logic [LANE_W-1:0] rand_lane; 49 | rand_lane = LANE_W'($random % LANE_N); 50 | for(int i=0; i < LANE_N; i++ ) begin 51 | tb_lane_i[i] = ( 1 << rand_lane ); 52 | `ifdef DEBUG 53 | $display("Rand lane set %d", tb_lane_i[i]); 54 | `endif 55 | rand_lane++; 56 | end 57 | 58 | endtask 59 | 60 | /* The following generate block isn't re-evaluated by verilator 61 | * every cycle if the left hand side is written in a task. 62 | * This isn't the case for iverilog, as such test fails with 63 | * verilator */ 64 | genvar x; 65 | generate 66 | for(x=0; x TX loopback 41 | * flop nreset for 2 cycle to have rx and tx gearbox 42 | * idle cycles in sync */ 43 | reg pcs_rx_nreset_q; 44 | reg pcs_tx_nreset_next; 45 | 46 | always @(posedge rx_clk) begin 47 | pcs_rx_nreset_q <= rx_nreset; 48 | end 49 | always @(posedge tx_clk) begin 50 | pcs_tx_nreset_next <= pcs_rx_nreset_q; 51 | tx_nreset <= pcs_tx_nreset_next; 52 | end 53 | 54 | /* Flop rx pcs output data */ 55 | reg [LANE_N-1:0] pcs_rx_ctrl_q; 56 | reg [LANE_N-1:0] pcs_rx_idle_q; 57 | reg [LANE_N-1:0] pcs_rx_term_q; 58 | reg [LANE_N-1:0] pcs_rx_err_q; 59 | reg [LANE_N*LANE0_CNT_N-1:0] pcs_rx_start_q; 60 | reg [LANE_N*DATA_W-1:0] pcs_rx_data_q; 61 | reg [LANE_N*KEEP_W-1:0] pcs_rx_keep_q; 62 | 63 | always @(posedge rx_clk) begin 64 | pcs_rx_ctrl_q <= pcs_rx_ctrl_i; 65 | pcs_rx_idle_q <= pcs_rx_idle_i; 66 | pcs_rx_term_q <= pcs_rx_term_i; 67 | pcs_rx_err_q <= pcs_rx_err_i; 68 | pcs_rx_start_q <= pcs_rx_start_i; 69 | pcs_rx_data_q <= pcs_rx_data_i; 70 | pcs_rx_keep_q <= pcs_rx_keep_i; 71 | end 72 | /* Empty cycle, both clks run at the 73 | * same frequencies but at different 74 | * phase */ 75 | always @(posedge tx_clk) begin 76 | pcs_tx_ctrl_o <= pcs_rx_ctrl_q; 77 | pcs_tx_idle_o <= pcs_rx_idle_q; 78 | pcs_tx_term_o <= pcs_rx_term_q; 79 | pcs_tx_err_o <= pcs_rx_err_q; 80 | pcs_tx_start_o <= pcs_rx_start_q; 81 | pcs_tx_data_o <= pcs_rx_data_q; 82 | pcs_tx_keep_o <= pcs_rx_keep_q; 83 | end 84 | 85 | end else begin : gen_not_10g 86 | /* Same clk for 40G, just flop the data 87 | * + no need to synchronize gearboxes, can 88 | * flop nreset alongside data */ 89 | always @(posedge clk) begin 90 | tx_nreset <= rx_nreset; 91 | pcs_tx_ctrl_o <= pcs_rx_ctrl_i; 92 | pcs_tx_idle_o <= pcs_rx_idle_i; 93 | pcs_tx_term_o <= pcs_rx_term_i; 94 | pcs_tx_err_o <= pcs_rx_err_i; 95 | pcs_tx_start_o <= pcs_rx_start_i; 96 | pcs_tx_data_o <= pcs_rx_data_i; 97 | pcs_tx_keep_o <= pcs_rx_keep_i; 98 | end 99 | end // IS_10G 100 | endgenerate 101 | endmodule 102 | -------------------------------------------------------------------------------- /xgmii_pcs_10g_enc_intf.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* interface between the 64b data wide xgmii interface and the pcs */ 9 | module xgmii_pcs_10g_enc_intf #( 10 | parameter XGMII_DATA_W = 64, 11 | parameter XGMII_KEEP_W = $clog2(XGMII_DATA_W), 12 | parameter XGMII_CTRL_W = 8, 13 | parameter BLOCK_W = 64, 14 | parameter HEAD_W = 2, // sync header 15 | 16 | parameter LANE0_CNT_N = BLOCK_W/( 4 * 8), 17 | parameter LANE0_CNT_W = $clog2(LANE0_CNT_N+1) 18 | 19 | )( 20 | input clk, 21 | input nreset, 22 | 23 | input [XGMII_DATA_W-1:0] xgmii_txd_i, 24 | input [XGMII_CTRL_W-1:0] xgmii_txc_i, 25 | 26 | output ctrl_v_o, 27 | output idle_v_o, 28 | output [LANE0_CNT_W-1:0] start_v_o, 29 | output term_v_o, 30 | output err_v_o, 31 | 32 | output [XGMII_KEEP_W-1:0] keep_o // data keep 33 | 34 | ); 35 | localparam [XGMII_CTRL_W-1:0] 36 | XGMII_CTRL_IDLE = 8'h07, 37 | XGMII_CTRL_START = 8'hfb, 38 | XGMII_CTRL_TERM = 8'hfd, 39 | XGMII_CTRL_ERR = 8'hfe; 40 | 41 | // translate xgmii control byte into individual signals 42 | assign ctrl_v_o = |xgmii_txc_i; 43 | 44 | // EOF 45 | // eof can be on any xgmii lane, but is the first control position 46 | // get the first ctrl bit 47 | logic [XGMII_CTRL_W-1:0] lsb_ctrl_mask; 48 | logic lsb_ctrl_mask_overflow; 49 | logic [XGMII_CTRL_W-1:0] lsb_ctrl; 50 | logic term_v_lite; 51 | assign {lsb_ctrl_mask_overflow, lsb_ctrl_mask} = ~xgmii_txc_i + {XGMII_CTRL_W-1{1'b0} , 1'b1}; 52 | always_comb begin 53 | for( int i=0; i < XGMII_CTRL_W; i++) begin 54 | if( lsb_ctrl_mask[i] ) lsb_ctrl = xgmii_txd_i[i*8+7:i*8]; 55 | end 56 | end 57 | assign term_v_lite = lsb_ctrl == XGMII_CTRL_TERM; 58 | 59 | // data mask for end of block 60 | logic [XGMII_KEEP_W-1:0] term_keep; 61 | assign term_keep = ~xgmii_txc_i; 62 | 63 | // SOF 64 | // sof only present on lane 0 65 | logic has_data; 66 | logic [XGMII_CTRL_W-1:0] msb_lane0_ctrl; 67 | logic [LANE0_CNT_N-1:0] lane0_start_v; 68 | logic [LANE0_CNT_N-1:0] start_v_lite; 69 | 70 | assign has_data = ~xgmii_txc_i[XGMII_CTRL_W-1]; 71 | genvar i; 72 | generate 73 | for(i=0; i 69 | 70 | 71 | 72 | 73 | 74 | } 75 | set_module_property FILE {cdc_fifo.ip} 76 | set_module_property GENERATION_ID {0x00000000} 77 | set_module_property NAME {cdc_fifo} 78 | 79 | # save the system 80 | sync_sysinfo_parameters 81 | save_system cdc_fifo 82 | } 83 | 84 | proc do_set_exported_interface_sysinfo_parameters {} { 85 | } 86 | 87 | # create all the systems, from bottom up 88 | do_create_cdc_fifo 89 | 90 | # set system info parameters on exported interface, from bottom up 91 | do_set_exported_interface_sysinfo_parameters 92 | -------------------------------------------------------------------------------- /am_lane_tx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Create marker per lane, keep track of Bit Interleaved Parity value 9 | * when not creating market */ 10 | module am_lane_tx #( 11 | parameter HEAD_W = 2, 12 | parameter DATA_W = 64, 13 | parameter BLOCK_W = HEAD_W + DATA_W, 14 | // fixed encoding for this lane 15 | parameter LANE_ENC = { {8{1'bx}},8'hb8, 8'h89,8'h6f,{8{1'bx}}, 8'h47, 8'h76, 8'h90 } 16 | )( 17 | input nreset, 18 | input clk, 19 | 20 | /* AM marker */ 21 | input marker_v,/* add alignement marker */ 22 | 23 | /* Gearbox */ 24 | input valid_i, 25 | 26 | /* Encoder, Scrambler */ 27 | input [BLOCK_W-1:0] data_i, 28 | 29 | /* Gearbox */ 30 | output [BLOCK_W-1:0] data_o 31 | ); 32 | localparam BIP_W = 8; 33 | localparam SYNC_HEAD_CTRL = 2'b10; 34 | 35 | reg [BIP_W-1:0] bip_q; 36 | logic [BIP_W-1:0] bip_next; 37 | logic [BIP_W-1:0] bip_pre; 38 | logic [BIP_W-1:0] bip3; 39 | logic [BIP_W-1:0] bip7; 40 | 41 | assign bip_pre = marker_v ? {BIP_W{1'b0}} : bip_q; 42 | 43 | always @(posedge clk) begin 44 | if ( ~nreset ) begin 45 | bip_q <= {BIP_W{1'b0}}; 46 | end else if ( valid_i ) begin 47 | bip_q <= bip_next; 48 | end 49 | end 50 | /* calculate bip 51 | BIP 3 bit number Assigned 66-bit word bits 52 | 0 2, 10, 18, 26, 34, 42, 50, 58 53 | 1 3, 11, 19, 27, 35, 43, 51, 59 54 | 2 4, 12, 20, 28, 36, 44, 52, 60 55 | 3 0, 5, 13, 21, 29, 37, 45, 53, 61 56 | 4 1, 6, 14, 22, 30, 38, 46, 54, 62 57 | 5 7, 15, 23, 31, 39, 47, 55, 63 58 | 6 8, 16, 24, 32, 40, 48, 56, 64 59 | 7 9, 17, 25, 33, 41, 49, 57, 65 60 | */ 61 | assign bip_next[0] = bip_pre[0] ^ 62 | data_o[2] ^ data_o[10] ^ data_o[18] ^ 63 | data_o[26] ^ data_o[34] ^ data_o[42] ^ 64 | data_o[50] ^ data_o[58]; 65 | 66 | assign bip_next[1] = bip_pre[1] ^ 67 | data_o[3] ^ data_o[11] ^ data_o[19] ^ 68 | data_o[27] ^ data_o[35] ^ data_o[43] ^ 69 | data_o[51] ^ data_o[59]; 70 | 71 | assign bip_next[2] = bip_pre[2] ^ 72 | data_o[4] ^ data_o[12] ^ data_o[20] ^ 73 | data_o[28] ^ data_o[36] ^ data_o[44] ^ 74 | data_o[52] ^ data_o[60]; 75 | assign bip_next[3] = bip_pre[3] ^ data_o[0] ^ data_o[5 ] ^ data_o[13] ^ data_o[21] ^ data_o[29] ^ data_o[37] ^ data_o[45] ^ data_o[53] ^ data_o[61]; 76 | assign bip_next[4] = bip_pre[4] ^ data_o[1] ^ data_o[6 ] ^ data_o[14] ^ data_o[22] ^ data_o[30] ^ data_o[38] ^ data_o[46] ^ data_o[54] ^ data_o[62]; 77 | assign bip_next[5] = bip_pre[5] ^ data_o[7] ^ data_o[15] ^ data_o[23] ^ data_o[31] ^ data_o[39] ^ data_o[47] ^ data_o[55] ^ data_o[63]; 78 | assign bip_next[6] = bip_pre[6] ^ data_o[8] ^ data_o[16] ^ data_o[24] ^ data_o[32] ^ data_o[40] ^ data_o[48] ^ data_o[56] ^ data_o[64]; 79 | assign bip_next[7] = bip_pre[7] ^ data_o[9] ^ data_o[17] ^ data_o[25] ^ data_o[33] ^ data_o[41] ^ data_o[49] ^ data_o[57] ^ data_o[65]; 80 | 81 | // bip calculation includes previous alignement marker and 82 | // all data in the gap 83 | assign bip3 = bip_q; 84 | assign bip7 = ~bip3; 85 | 86 | // create new marker 87 | logic [DATA_W-1:0] marker_data; 88 | logic [HEAD_W-1:0] market_head; 89 | 90 | assign market_head = SYNC_HEAD_CTRL; 91 | genvar i; 92 | generate 93 | for( i = 0; i < 8; i++ ) begin : gen_marker_data_loop 94 | if ( i == 3 ) begin : gen_i_eq_3 95 | assign marker_data[i*8+7:i*8] = bip3; 96 | end else if ( i == 7 ) begin : gen_i_eq_7 97 | assign marker_data[i*8+7:i*8] = bip7; 98 | end else begin : gen_i_default 99 | assign marker_data[i*8+7:i*8] = LANE_ENC[i*8+7:i*8]; 100 | end 101 | end 102 | endgenerate 103 | 104 | // output 105 | assign data_o = marker_v ? { marker_data, market_head } : data_i; 106 | endmodule 107 | 108 | 109 | -------------------------------------------------------------------------------- /tb/vpi/vpi_i/tb_pcs.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "tb_pcs.h" 9 | #include "../tb_pcs_common.h" 10 | #include "../tv.h" 11 | #include 12 | #include 13 | #include 14 | 15 | static tv_t *tv_s = NULL; 16 | 17 | static int tb_compiletf(char*user_data) 18 | { 19 | #ifdef DEBUG 20 | vpi_printf("TB compile\n"); 21 | #endif 22 | tv_s = tv_alloc(); 23 | return 0; 24 | } 25 | 26 | // Drive PCS input values 27 | static int tb_calltf(char*user_data) 28 | { 29 | vpiHandle sys; 30 | vpiHandle argv; 31 | 32 | sys = vpi_handle(vpiSysTfCall, 0); 33 | assert(sys); 34 | argv = vpi_iterate(vpiArgument, sys); 35 | assert(argv); 36 | #ifdef DEBUG 37 | vpi_printf("TB call\n"); 38 | #endif 39 | 40 | assert(tv_s); 41 | 42 | // handlers : the order matters 43 | vpiHandle h_ready_o = vpi_scan(argv); 44 | vpiHandle h_ctrl_v_i= vpi_scan(argv); 45 | vpiHandle h_idle_v_i= vpi_scan(argv); 46 | vpiHandle h_start_v_i= vpi_scan(argv); 47 | vpiHandle h_term_v_i= vpi_scan(argv); 48 | vpiHandle h_term_keep_i= vpi_scan(argv); 49 | vpiHandle h_err_i= vpi_scan(argv); 50 | vpiHandle h_data_i= vpi_scan(argv); 51 | vpiHandle h_debug_id_i= vpi_scan(argv); 52 | 53 | 54 | tb_pcs_tx(tv_s, 55 | h_ready_o, 56 | h_ctrl_v_i, 57 | h_idle_v_i, 58 | h_start_v_i, 59 | h_term_v_i, 60 | h_term_keep_i, 61 | h_err_i, 62 | h_data_i, 63 | h_debug_id_i); 64 | 65 | return 0; 66 | } 67 | void tb_register() 68 | { 69 | s_vpi_systf_data tf_data; 70 | 71 | tf_data.type = vpiSysTask; 72 | tf_data.sysfunctype = 0; 73 | tf_data.tfname = "$tb"; 74 | tf_data.calltf = tb_calltf; 75 | tf_data.compiletf = tb_compiletf; 76 | tf_data.sizetf = 0; 77 | tf_data.user_data = 0; 78 | vpi_register_systf(&tf_data); 79 | } 80 | 81 | 82 | static int tb_exp_compiletf(char *path) 83 | { 84 | return 0; 85 | } 86 | /* Produce the expected output of the PCS and write it 87 | * to signals passed as parameter */ 88 | static PLI_INT32 tb_exp_calltf( 89 | char *user_data 90 | ) 91 | { 92 | vpiHandle sys = vpi_handle(vpiSysTfCall, 0); 93 | assert(sys); 94 | vpiHandle argv = vpi_iterate(vpiArgument, sys); 95 | assert(argv); 96 | 97 | vpiHandle h_pma_o = vpi_scan(argv); 98 | vpiHandle h_debug_id_o = vpi_scan(argv); 99 | 100 | // assert 101 | assert(h_pma_o); 102 | assert(h_debug_id_o); 103 | 104 | tb_pcs_tx_exp(tv_s, h_pma_o, h_debug_id_o); 105 | 106 | return 0; 107 | } 108 | 109 | void tb_exp_register() 110 | { 111 | s_vpi_systf_data tf_end_data; 112 | 113 | tf_end_data.type = vpiSysFunc; 114 | tf_end_data.sysfunctype = vpiSysFuncInt; 115 | tf_end_data.tfname = "$tb_exp"; 116 | tf_end_data.calltf = tb_exp_calltf; 117 | tf_end_data.compiletf = tb_exp_compiletf; 118 | tf_end_data.sizetf = 0; 119 | tf_end_data.user_data = 0; 120 | vpi_register_systf(&tf_end_data); 121 | } 122 | 123 | 124 | 125 | // de-init routine 126 | static int tb_end_compiletf(char *path) 127 | { 128 | return 0; 129 | } 130 | 131 | static PLI_INT32 tb_end_calltf(char*user_data){ 132 | if ( tv_s != NULL)tv_free(tv_s); 133 | return 0; 134 | } 135 | 136 | void tb_end_register() 137 | { 138 | s_vpi_systf_data tf_end_data; 139 | 140 | tf_end_data.type = vpiSysFunc; 141 | tf_end_data.sysfunctype = vpiSysFuncInt; 142 | tf_end_data.tfname = "$tb_end"; 143 | tf_end_data.calltf = tb_end_calltf; 144 | tf_end_data.compiletf = tb_end_compiletf; 145 | tf_end_data.sizetf = 0; 146 | tf_end_data.user_data = 0; 147 | vpi_register_systf(&tf_end_data); 148 | } 149 | 150 | 151 | void (*vlog_startup_routines[])() = { 152 | tb_end_register, 153 | tb_exp_register, 154 | tb_register, 155 | 0 156 | }; 157 | 158 | -------------------------------------------------------------------------------- /tb/vpi/vpi_v/tb_pcs.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #include "tb_pcs.hpp" 8 | #include "verilated.h" 9 | #include "verilated_vpi.h" // Required to get definitions 10 | #include "../tb_pcs_common.h" 11 | 12 | #if VM_TRACE 13 | # include // Trace file format header 14 | #endif 15 | 16 | 17 | uint64_t main_time = 0; // See comments in first example 18 | double sc_time_stamp() { return main_time; } 19 | 20 | 21 | int main(int argc, char** argv) { 22 | Verilated::commandArgs(argc, argv); 23 | tv_t *tv_s = tv_alloc(); 24 | 25 | const std::unique_ptr contextp{new VerilatedContext}; 26 | const std::unique_ptr top{new VMODULE{contextp.get()}}; 27 | 28 | #if VM_TRACE // makefile wave invoked with wave=1 29 | Verilated::traceEverOn(true);// computer trace signals 30 | VL_PRINTF("Enabling waves...\n"); 31 | VerilatedVcdC* tfp = new VerilatedVcdC; 32 | top->trace (tfp, 15);// trace 15 levels of hierachy 33 | tfp->open ("wave/"STR(MODULE)".vcd"); // Open the dump file 34 | #endif 35 | 36 | 37 | //contextp->internalsDump(); // See scopes to help debug 38 | 39 | // vpiHandlers 40 | vpiHandle h_ready_o = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".ready_o", NULL); 41 | vpiHandle h_ctrl_v_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".ctrl_v_i", NULL); 42 | vpiHandle h_idle_v_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".idle_v_i", NULL); 43 | vpiHandle h_start_v_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".start_v_i", NULL); 44 | vpiHandle h_term_v_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".term_v_i", NULL); 45 | vpiHandle h_keep_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".keep_i", NULL); 46 | vpiHandle h_err_v_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".err_v_i", NULL); 47 | vpiHandle h_data_i = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".data_i", NULL); 48 | vpiHandle h_debug_id = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".data_debug_id", NULL); 49 | 50 | vpiHandle h_tb_gb = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".tb_gb_data", NULL); 51 | vpiHandle h_tb_gb_debug_id = vpi_handle_by_name((PLI_BYTE8*)"TOP."STR(MODULE)".tb_data_debug_id", NULL); 52 | 53 | /* reset sequence 54 | * Hold reset for 1 clk cycle -> 10 C cycles */ 55 | for(; main_time<18; main_time++){ 56 | top->eval(); 57 | #if VM_TRACE 58 | if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp 59 | #endif 60 | } 61 | while (!contextp->gotFinish()) { 62 | top->eval(); 63 | VerilatedVpi::callValueCbs(); // For signal callbacks 64 | if ( main_time % 10 == 0 ){ 65 | // pcs 66 | tb_pcs_tx(tv_s, 67 | h_ready_o, 68 | h_ctrl_v_i, 69 | h_idle_v_i, 70 | h_start_v_i, 71 | h_term_v_i, 72 | h_keep_i, 73 | h_err_v_i, 74 | h_data_i, 75 | h_debug_id); 76 | // exp 77 | tb_pcs_tx_exp(tv_s, 78 | h_tb_gb, 79 | h_tb_gb_debug_id); 80 | } 81 | 82 | // TODO remove bellow 83 | //if( main_time >= 168800 ) abort(); 84 | 85 | #if VM_TRACE 86 | if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp 87 | //if ( main_time >= 163800 )if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp 88 | #endif 89 | 90 | main_time++; 91 | } 92 | 93 | 94 | #if VM_TRACE 95 | if (tfp) tfp->close(); 96 | #endif 97 | 98 | // free 99 | vpi_release_handle(h_ready_o); 100 | vpi_release_handle(h_ctrl_v_i ); 101 | vpi_release_handle(h_idle_v_i ); 102 | vpi_release_handle(h_start_v_i); 103 | vpi_release_handle(h_term_v_i ); 104 | vpi_release_handle(h_keep_i ); 105 | vpi_release_handle(h_err_v_i); 106 | vpi_release_handle(h_data_i ); 107 | vpi_release_handle(h_debug_id ); 108 | 109 | tv_free(tv_s); 110 | 111 | top->final(); 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /tb/vpi/pcs_defs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | #ifndef PCS_DEFS_H 8 | #define PCS_DEFS_H 9 | #include 10 | #include 11 | #include 12 | 13 | #define TXD_W 8 14 | 15 | #ifdef _40GBASE 16 | #define START_W 1 17 | #define LANE_N 4 18 | #define MARKER_GAP_N 16383 19 | #else 20 | #define START_W 2 21 | #define LANE_N 1 22 | #endif 23 | #define XGMII_CTRL_IDLE (uint8_t) 0x07 24 | #define XGMII_CTRL START (uint8_t) 0xfb 25 | #define XGMII_CTRL_TERMINATE (uint8_t) 0xfb 26 | #define XGMII_CTRL_ERROR (uint8_t) 0xfe 27 | 28 | #define BLOCK_TYPE_CTRL (uint8_t) 0x1e // C7 C6 C5 C4 C3 C2 C1 C0 BT 29 | #define BLOCK_TYPE_OS_4 (uint8_t) 0x2d // D7 D6 D5 O4 C3 C2 C1 C0 BT 30 | #define BLOCK_TYPE_START_4 (uint8_t) 0x33 // D7 D6 D5 C3 C2 C1 C0 BT 31 | #define BLOCK_TYPE_OS_START (uint8_t) 0x66 // D7 D6 D5 O0 D3 D2 D1 BT 32 | #define BLOCK_TYPE_OS_04 (uint8_t) 0x55 // D7 D6 D5 O4 O0 D3 D2 D1 BT 33 | #define BLOCK_TYPE_START_0 (uint8_t) 0x78 // D7 D6 D5 D4 D3 D2 D1 BT 34 | #define BLOCK_TYPE_OS_0 (uint8_t) 0x4b // C7 C6 C5 C4 O0 D3 D2 D1 BT 35 | #define BLOCK_TYPE_TERM_0 (uint8_t) 0x87 // C7 C6 C5 C4 C3 C2 C1 BT 36 | #define BLOCK_TYPE_TERM_1 (uint8_t) 0x99 // C7 C6 C5 C4 C3 C2 D0 BT 37 | #define BLOCK_TYPE_TERM_2 (uint8_t) 0xaa // C7 C6 C5 C4 C3 D1 D0 BT 38 | #define BLOCK_TYPE_TERM_3 (uint8_t) 0xb4 // C7 C6 C5 C4 D2 D1 D0 BT 39 | #define BLOCK_TYPE_TERM_4 (uint8_t) 0xcc // C7 C6 C5 D3 D2 D1 D0 BT 40 | #define BLOCK_TYPE_TERM_5 (uint8_t) 0xd2 // C7 C6 D4 D3 D2 D1 D0 BT 41 | #define BLOCK_TYPE_TERM_6 (uint8_t) 0xe1 // C7 D5 D4 D3 D2 D1 D0 BT 42 | #define BLOCK_TYPE_TERM_7 (uint8_t) 0xff // D6 D5 D4 D3 D2 D1 D0 BT 43 | 44 | #define BLOCK_CTRL_IDLE (uint64_t) 0x00 45 | #define BLOCK_CTRL_ERR (uint64_t) 0b00111100011110001111000111100011110001111000111100011110// 8{7'h1e} 46 | 47 | #define SYNC_HEAD_CTRL (uint8_t) 0x02 //10b 48 | #define SYNC_HEAD_DATA (uint8_t) 0x01 //01b 49 | 50 | #define I0_64b66b 38 51 | #define I1_64b66b 57 52 | 53 | #define MARK_LANE0 {0x90, 0x76, 0x47, bip3, 0x6F, 0x89, 0xB8, bip7} 54 | #define MARK_LANE1 {0xF0, 0xC4, 0xE6, bip3, 0x0F, 0x3B, 0x19, bip7} 55 | #define MARK_LANE2 {0xC5, 0x65, 0x9B, bip3, 0x3A, 0x9A, 0x64, bip7} 56 | #define MARK_LANE3 {0xA2, 0x79, 0x3D, bip3, 0x5D, 0x86, 0xC2, bip7} 57 | #define MARK_LANE_ARR { MARK_LANE0, MARK_LANE1, MARK_LANE2, MARK_LANE3 } 58 | 59 | typedef unsigned __int128 uint128_t; 60 | 61 | typedef struct { 62 | uint8_t head; 63 | uint64_t data; 64 | }block_s; 65 | 66 | typedef struct{ 67 | bool ctrl_v; 68 | bool idle_v; 69 | bool start_v[START_W]; 70 | bool term_v; 71 | uint8_t term_keep; 72 | bool err_v; 73 | }ctrl_lite_s; 74 | 75 | typedef struct{ 76 | uint64_t buff[2]; 77 | size_t len; 78 | }gearbox_s; 79 | 80 | typedef struct{ 81 | uint8_t bip[LANE_N]; 82 | uint16_t gap; 83 | }marker_s; 84 | 85 | typedef struct{ 86 | block_s block_enc; 87 | // only 1 scrambler shared amoung all lanes 88 | uint64_t scrambler_state; 89 | block_s block_scram; 90 | #ifdef _40GBASE 91 | size_t lane_idx; //current lane index 92 | marker_s marker_state; 93 | block_s block_mark[LANE_N]; 94 | #endif 95 | gearbox_s gearbox_state[LANE_N]; 96 | }pcs_tx_s; 97 | 98 | #define LEN_TO_KEEP(len,x) switch (len) {\ 99 | case 0: x = 0x00; \ 100 | break; \ 101 | case 1: x = 0x01; \ 102 | break; \ 103 | case 2: x = 0x03; \ 104 | break; \ 105 | case 3: x = 0x07; \ 106 | break; \ 107 | case 4: x = 0x0f; \ 108 | break; \ 109 | case 5: x = 0x1f; \ 110 | break; \ 111 | case 6: x = 0x3f; \ 112 | break; \ 113 | case 7: x = 0x7f; \ 114 | break; \ 115 | case 8: x = 0xff; \ 116 | break; \ 117 | default : printf("undexpected length : %ld.\n", len); assert(0);\ 118 | } 119 | 120 | #endif // PCS_DEFS_H 121 | -------------------------------------------------------------------------------- /gearbox_tx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Generic circular buffer 9 | * Expected parameters for data widths : 16,32, 64 10 | * Tested parameters : 16, 64 */ 11 | module gearbox_tx #( 12 | parameter DATA_W = 64, 13 | parameter HEAD_W = 2 14 | )( 15 | input clk, 16 | input nreset, 17 | 18 | input [HEAD_W-1:0] head_i, // sync header 19 | input [DATA_W-1:0] data_i, 20 | 21 | // gearbox output 22 | output accept_v_o, // backpressure, buffer is full, need a cycle to clear 23 | output [DATA_W-1:0] data_o 24 | ); 25 | localparam FIFO_W = DATA_W; 26 | localparam SHIFT_N = DATA_W/HEAD_W; 27 | localparam SEQ_W = $clog2(DATA_W/HEAD_W+1); 28 | 29 | 30 | logic fifo_full; 31 | logic seq_rst; 32 | logic [SEQ_W-1:0] seq_next; 33 | logic [SEQ_W-1:0] seq_inc; 34 | logic unused_seq_inc_of; 35 | reg [SEQ_W-1:0] seq_q; 36 | 37 | assign fifo_full = seq_q[SEQ_W-1]; 38 | 39 | /* seq fifo fill : gearbox state */ 40 | assign seq_rst = fifo_full; 41 | assign { unused_seq_inc_of, seq_inc } = seq_q + {{SEQ_W-1{1'b0}} , 1'b1}; 42 | assign seq_next = seq_rst ? {SEQ_W{1'b0}} : seq_inc; 43 | 44 | always @(posedge clk) begin 45 | if ( ~nreset ) begin 46 | seq_q <= { SEQ_W{1'b0}}; 47 | end else begin 48 | seq_q <= seq_next; 49 | end 50 | end 51 | 52 | // current fifo depth is derrived from the sequence number 53 | logic [FIFO_W-1:0] fifo_next; 54 | reg [FIFO_W-1:0] fifo_q; 55 | 56 | // shift data 57 | localparam MASK_ARR_W = FIFO_W / HEAD_W; 58 | 59 | logic [SHIFT_N:0] shift_sel; 60 | 61 | logic [FIFO_W-1:0] wr_fifo_shifted_arr[SHIFT_N-1:0]; 62 | logic [FIFO_W-1:0] rd_data_shifted_arr[SHIFT_N-1:0]; 63 | logic [FIFO_W-1:0] wr_fifo_shifted; 64 | logic [FIFO_W-1:0] rd_data_shifted; 65 | 66 | genvar i; 67 | generate 68 | /* shift sel : dec to bin onehot */ 69 | for( i = 0; i <=SHIFT_N; i++) begin : shift_sel_loop 70 | assign shift_sel[i] = ( seq_q == i ); 71 | end 72 | 73 | /* read and write data shifted */ 74 | for( i = 0; i < SHIFT_N; i++ ) begin : rd_data_shifted_loop 75 | /* block data to be written to fifo 76 | * seq 0 : write 2 msb bits to fifo 77 | * seq 1 : write 4 msb bits to fifo 78 | * ... 79 | * seq 31 : write 64 msb bits to fifo 80 | * seq 32 : X */ 81 | assign wr_fifo_shifted_arr[i] = { {DATA_W-(i+1)*HEAD_W{1'bx}} , data_i[DATA_W-1:DATA_W - (i+1)*HEAD_W] }; 82 | 83 | if ( i != SHIFT_N-1 ) begin : gen_lt_SHIFT_N 84 | assign rd_data_shifted_arr[i] = { data_i[DATA_W-HEAD_W-i*HEAD_W-1:0], head_i , {i*HEAD_W{1'bx}}} ; 85 | //assign rd_data_shifted_arr[0] = { data_i[DATA_W-HEAD_W-i*HEAD_W-1:0], head_i } ; 86 | end else begin : gen_eq_SHIFT_N 87 | assign rd_data_shifted_arr[i] = { head_i , {i*HEAD_W{1'bx}} } ; 88 | end 89 | end 90 | endgenerate 91 | 92 | assign wr_fifo_shifted = wr_fifo_shifted_arr[seq_q[SEQ_W-2:0]]; 93 | assign rd_data_shifted = rd_data_shifted_arr[seq_q[SEQ_W-2:0]]; 94 | 95 | /* read fifo mask 96 | * seq 0 : nothing from fifo 97 | * seq 1 : read 2 bytes from fifo ( 1 i lite mask version ) 98 | * seq 2 : read 4 bytes from fifo 99 | * ... 100 | * seq 32 : read 64 bytes from fifo ( entire content ) */ 101 | logic [MASK_ARR_W-1:0] rd_fifo_mask_lite; 102 | logic unused_rd_fifo_mask_lite; 103 | 104 | assign { unused_rd_fifo_mask_lite, rd_fifo_mask_lite } = shift_sel - {{SHIFT_N-1{1'b0}}, 1'b1}; 105 | 106 | // extend masks 107 | logic [FIFO_W-1:0] rd_fifo_mask; // full version of the mask 108 | generate 109 | for( i = 0; i < MASK_ARR_W; i++ ) begin : mask_loop 110 | assign rd_fifo_mask[i*HEAD_W+HEAD_W-1:i*HEAD_W] = {HEAD_W{rd_fifo_mask_lite[i] }}; 111 | end 112 | endgenerate 113 | 114 | // buf data 115 | assign fifo_next = wr_fifo_shifted; 116 | always @(posedge clk) begin 117 | fifo_q <= fifo_next; 118 | end 119 | 120 | // buffer is full, tell mac to not send next cycle 121 | assign accept_v_o = ~fifo_full; 122 | 123 | assign data_o = rd_fifo_mask & fifo_q | ~rd_fifo_mask & rd_data_shifted; 124 | endmodule 125 | -------------------------------------------------------------------------------- /tcl/phy_rst.tcl: -------------------------------------------------------------------------------- 1 | package require qsys 2 | 3 | # create the system "phy_rst" 4 | proc do_create_phy_rst {} { 5 | # create the system 6 | create_system phy_rst 7 | 8 | set_project_property DEVICE {10CX150YF780E5G} 9 | set_project_property DEVICE_FAMILY {Cyclone 10 GX} 10 | set_project_property HIDE_FROM_IP_CATALOG {true} 11 | set_use_testbench_naming_pattern 0 {} 12 | 13 | # add HDL parameters 14 | 15 | # add the components 16 | add_instance xcvr_reset_control_0 altera_xcvr_reset_control 19.1.1 17 | set_instance_parameter_value xcvr_reset_control_0 {CHANNELS} {1} 18 | set_instance_parameter_value xcvr_reset_control_0 {PLLS} {1} 19 | set_instance_parameter_value xcvr_reset_control_0 {REDUCED_SIM_TIME} {1} 20 | set_instance_parameter_value xcvr_reset_control_0 {RX_ENABLE} {1} 21 | set_instance_parameter_value xcvr_reset_control_0 {RX_PER_CHANNEL} {0} 22 | set_instance_parameter_value xcvr_reset_control_0 {SYNCHRONIZE_PLL_RESET} {0} 23 | set_instance_parameter_value xcvr_reset_control_0 {SYNCHRONIZE_RESET} {1} 24 | set_instance_parameter_value xcvr_reset_control_0 {SYS_CLK_IN_MHZ} {50} 25 | set_instance_parameter_value xcvr_reset_control_0 {TX_ENABLE} {1} 26 | set_instance_parameter_value xcvr_reset_control_0 {TX_PER_CHANNEL} {0} 27 | set_instance_parameter_value xcvr_reset_control_0 {TX_PLL_ENABLE} {1} 28 | set_instance_parameter_value xcvr_reset_control_0 {T_PLL_LOCK_HYST} {0} 29 | set_instance_parameter_value xcvr_reset_control_0 {T_PLL_POWERDOWN} {1000} 30 | set_instance_parameter_value xcvr_reset_control_0 {T_RX_ANALOGRESET} {40} 31 | set_instance_parameter_value xcvr_reset_control_0 {T_RX_DIGITALRESET} {4000} 32 | set_instance_parameter_value xcvr_reset_control_0 {T_TX_ANALOGRESET} {0} 33 | set_instance_parameter_value xcvr_reset_control_0 {T_TX_DIGITALRESET} {20} 34 | set_instance_parameter_value xcvr_reset_control_0 {gui_pll_cal_busy} {0} 35 | set_instance_parameter_value xcvr_reset_control_0 {gui_rx_auto_reset} {0} 36 | set_instance_parameter_value xcvr_reset_control_0 {gui_split_interfaces} {1} 37 | set_instance_parameter_value xcvr_reset_control_0 {gui_tx_auto_reset} {0} 38 | set_instance_property xcvr_reset_control_0 AUTO_EXPORT true 39 | 40 | # add wirelevel expressions 41 | 42 | # preserve ports for debug 43 | 44 | # add the exports 45 | set_interface_property clock EXPORT_OF xcvr_reset_control_0.clock 46 | set_interface_property reset EXPORT_OF xcvr_reset_control_0.reset 47 | set_interface_property pll_powerdown0 EXPORT_OF xcvr_reset_control_0.pll_powerdown0 48 | set_interface_property tx_analogreset0 EXPORT_OF xcvr_reset_control_0.tx_analogreset0 49 | set_interface_property tx_digitalreset0 EXPORT_OF xcvr_reset_control_0.tx_digitalreset0 50 | set_interface_property tx_ready0 EXPORT_OF xcvr_reset_control_0.tx_ready0 51 | set_interface_property pll_locked0 EXPORT_OF xcvr_reset_control_0.pll_locked0 52 | set_interface_property pll_select EXPORT_OF xcvr_reset_control_0.pll_select 53 | set_interface_property tx_cal_busy0 EXPORT_OF xcvr_reset_control_0.tx_cal_busy0 54 | set_interface_property rx_analogreset0 EXPORT_OF xcvr_reset_control_0.rx_analogreset0 55 | set_interface_property rx_digitalreset0 EXPORT_OF xcvr_reset_control_0.rx_digitalreset0 56 | set_interface_property rx_ready0 EXPORT_OF xcvr_reset_control_0.rx_ready0 57 | set_interface_property rx_is_lockedtodata0 EXPORT_OF xcvr_reset_control_0.rx_is_lockedtodata0 58 | set_interface_property rx_cal_busy0 EXPORT_OF xcvr_reset_control_0.rx_cal_busy0 59 | 60 | # set values for exposed HDL parameters 61 | 62 | # set the the module properties 63 | set_module_property BONUS_DATA { 64 | 65 | 66 | 67 | 68 | 69 | } 70 | set_module_property FILE {phy_rst.ip} 71 | set_module_property GENERATION_ID {0x00000000} 72 | set_module_property NAME {phy_rst} 73 | 74 | # save the system 75 | sync_sysinfo_parameters 76 | save_system phy_rst 77 | } 78 | 79 | proc do_set_exported_interface_sysinfo_parameters {} { 80 | } 81 | 82 | # create all the systems, from bottom up 83 | do_create_phy_rst 84 | 85 | # set system info parameters on exported interface, from bottom up 86 | do_set_exported_interface_sysinfo_parameters 87 | -------------------------------------------------------------------------------- /tb/README.md: -------------------------------------------------------------------------------- 1 | # Test bench 2 | 3 | This directory contains a collection of self checking unit and top level testing suite 4 | targeting our PCS implementation. 5 | It is built from the top level Makefile. 6 | 7 | ## Overview 8 | 9 | Top level test bench : 10 | 11 | - `pcs_tb.sv` covers `rx` and `tx` is driven via the `vpi` by `C/C++` code contained in the `vpi` folder 12 | 13 | Unit test benches : 14 | 15 | - `am_tx_tb.sv` `tx` alignment marker content check, uses `vpi` and expected results are provided by `C/C++` code. 16 | 17 | - `block_sync_rx_tb.sv` checks `rx` block lock follows behaviour outlines in clause 82.12 18 | 19 | - `lane_reorder_rx_tb.sv` `rx` lane reordering check 20 | 21 | - `xgmii_dec_rx_tb.sv` check translation from internal decoded 22 | representation to standard xgmii representation, used for compatibility 23 | with third party MACs. 24 | 25 | - `_64b66b_tb.sv` checks scrambler and descrambler behavior, data going though 26 | scrambler then descrambler should match original 27 | 28 | - `pcs_10g_enc_tb.sv` check xgmii to internal representation interface behavior, 29 | this is the `tx` counterpart to the `rx` `xgmii_dec_rx_tb` 30 | 31 | - `am_lock_rx_tb.sv` check `rx` alignment locking matches behavior outlined in clause 82.13 32 | 33 | - `deskew_rx_tb.sv` check skew compensation and lane realignment on `rx` path 34 | 35 | - `gearbox_tx_tb.sv` check `tx` path 66b to 64b gearbox 36 | 37 | 38 | ### Simulators 39 | 40 | This project support **2 simulators** `iverilog` and `verilator`. 41 | We recommend using `verilator` for speed. 42 | 43 | To switch between simulators set the `SIM` variable when invoking make. 44 | ``` 45 | make SIM= [...] 46 | ``` 47 | 48 | By default `iverilog` will be used. 49 | 50 | `SIM` values : 51 | 52 | - `I` : use `iverilog` 53 | 54 | - `V` : use `verilator` 55 | 56 | ### Get waves 57 | 58 | Waves will be written to files in the root `wave` directory. 59 | 60 | ### Other options 61 | 62 | To disable waves : 63 | ``` 64 | make wave= [...] 65 | ``` 66 | 67 | To disable fail on asserts when using verilator asserts : 68 | ``` 69 | make assert= [....] 70 | ``` 71 | 72 | To enable debug logs 73 | ``` 74 | make clean 75 | make debug=1 [...] 76 | ``` 77 | ## Unit test 78 | 79 | Commands for running the none `vpi` unit test benches. 80 | 81 | #### scrambler / decrambler 82 | ``` 83 | make run__64b66b 84 | ``` 85 | 86 | #### TX gearbox 87 | ``` 88 | make run_gearbox_tx 89 | ``` 90 | 91 | #### RX block sync 92 | ``` 93 | make run_block_sync_rx 94 | ``` 95 | 96 | #### RX Alignment marker lock 97 | ``` 98 | make run_am_lock_rx 99 | ``` 100 | 101 | #### RX lane reordering 102 | ``` 103 | make run_lane_reorder_rx 104 | ``` 105 | 106 | #### RX xgmii decoder interface 107 | ``` 108 | make run_xgmii_dec_rx 109 | ``` 110 | 111 | #### RX lane deskew 112 | ``` 113 | make run_deskew_rx 114 | ``` 115 | 116 | ## VPI 117 | 118 | **Before** running and test benches using the `vpi` please set the path to the 119 | `vpi` library.. 120 | 121 | ### Setup VPI library 122 | 123 | Some of our test benches use a golden model coded in `C/C++` and uses the `vpi` to interface 124 | with our system verilog test bench. 125 | These golden models will be compiled into a dynamically relocatable library that will 126 | be loaded at run time by the simulator. 127 | 128 | Taking into account the simulator you will be using check the path to the simulator library 129 | in the `tb/vpi/Makefile` through the `VPI_INC` variable. 130 | 131 | 132 | ### Full 40Gbe PCS 133 | 134 | Full test bench for the 4 lane, 64 bits per lane, 40GBASE-R. 135 | I recommend using verilator to speed up this test. 136 | 137 | To build a run: 138 | ``` 139 | make clean 140 | make SIM=V run_pcs 141 | ``` 142 | 143 | ## 40Gbe PCS alignment marker 144 | 145 | Partial test bench covering only the alignment marker logic. 146 | Although this block is also tested under the full 40Gbe PCS test bench, 147 | because the alignment marker is only added on a per lane bases every 16383 blocks 148 | I have decided to create a special test bench targeting only this feature to speed 149 | up iterations. 150 | 151 | To build and run : 152 | ``` 153 | make clean 154 | make run_am_tx 155 | ``` 156 | 157 | 158 | -------------------------------------------------------------------------------- /gearbox_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Rx gearbox, accepts 64 bits and produces 66 */ 9 | module gearbox_rx #( 10 | parameter HEAD_W = 2, 11 | parameter DATA_W = 64 12 | )( 13 | input clk, 14 | input nreset, 15 | 16 | /* pma */ 17 | input lock_v_i, 18 | input [DATA_W-1:0] data_i, 19 | /* block sync */ 20 | input slip_v_i, 21 | 22 | /* to block sync */ 23 | output valid_o, // output data is valid : backpressure, buffer is full, need a cycle to clear 24 | output [HEAD_W-1:0] head_o, 25 | output [DATA_W-1:0] data_o 26 | ); 27 | localparam BLOCK_W = DATA_W + HEAD_W; 28 | 29 | localparam FIFO_W = DATA_W; 30 | localparam SHIFT_N = FIFO_W; 31 | 32 | localparam CNT_W = $clog2(SHIFT_N+1); 33 | 34 | 35 | logic [FIFO_W-1:0] wr_fifo_shifted_arr[SHIFT_N-1:0]; 36 | logic [FIFO_W-1:0] wr_fifo_shifted; // write data to write into fifo register 37 | 38 | logic [BLOCK_W-1:0] rd_data_shifted_arr[SHIFT_N:1]; 39 | logic [BLOCK_W-1:0] rd_data_shifted; 40 | 41 | genvar i; 42 | generate 43 | for( i = 0; i < SHIFT_N; i++ ) begin : wr_fifo_shifted_loop 44 | /* wr fifo data shifted 45 | * seq_q 0 : write all of data into fifo */ 46 | assign wr_fifo_shifted_arr[i] = { {i{1'bx}}, data_i[DATA_W-1:i] }; 47 | end 48 | for( i = 1; i <= SHIFT_N; i++ ) begin : rd_shifted_loop 49 | // rd data and mask 50 | assign rd_data_shifted_arr[i] = { data_i[0+:i], {BLOCK_W-i{1'bx}}}; 51 | end 52 | 53 | endgenerate 54 | 55 | // sequence 56 | localparam INC_W = $clog2( HEAD_W + 2); 57 | reg [CNT_W-1:0] seq_q; 58 | logic [CNT_W-1:0] seq_next; 59 | logic [CNT_W-1:0] seq_xor; 60 | logic seq_rst; 61 | logic [CNT_W-1:0] seq_add; 62 | logic [INC_W-1:0] seq_inc; 63 | logic unused_seq_add_of; 64 | 65 | /* increment by 2 or 3 sequence counter bepending on if we are 66 | * slipping the lsb */ 67 | assign seq_inc = { 1'b1, slip_v_i }; 68 | assign {unused_seq_add_of, seq_add } = seq_q + { {CNT_W-INC_W{1'b0}}, seq_inc }; 69 | 70 | /* reset sequence */ 71 | assign seq_rst = ~lock_v_i; 72 | 73 | /* reset to zero, and set next to mod % 64 when seq_q >= 64 to keep slip offset */ 74 | assign seq_next = {CNT_W{~seq_rst}} & (seq_add & ~{{CNT_W-1{seq_q[CNT_W-1]}},1'b0}) ; 75 | 76 | always @(posedge clk) begin 77 | if ( ~nreset ) begin 78 | seq_q <= {CNT_W{1'b0}}; 79 | end else begin 80 | seq_q <= seq_next; 81 | end 82 | end 83 | 84 | logic [SHIFT_N:0] shift_sel; 85 | generate 86 | for( i = 0; i <= SHIFT_N; i++) begin : shift_sel_loop 87 | /* verilator lint_off WIDTHEXPAND */ 88 | assign shift_sel[i] = ( seq_q == i ); 89 | /* verilator lint_on WIDTHEXPAND */ 90 | end 91 | 92 | logic unused_rd_data_mask_rev; 93 | logic [DATA_W-1:0] rd_data_mask_rev; // reversed verson of the data mask 94 | logic [BLOCK_W-1:0] rd_data_mask; // full version of the mask 95 | 96 | assign {unused_rd_data_mask_rev, rd_data_mask_rev} = shift_sel - {{DATA_W-1{1'b0}}, 1'b1}; 97 | 98 | assign rd_data_mask[HEAD_W-1:0] = {HEAD_W{1'b0}}; 99 | for(i=HEAD_W; i< BLOCK_W; i++) begin: rd_data_mask_reverse 100 | assign rd_data_mask[i] = rd_data_mask_rev[BLOCK_W-i-1]; 101 | end 102 | 103 | endgenerate 104 | 105 | always_comb begin : rd_wr_shift_sel 106 | 107 | // rd mask and data 108 | rd_data_shifted = {BLOCK_W{1'bx}}; 109 | for( int x=1; x <=SHIFT_N; x++) begin 110 | /* setting default state to prevent latch inference */ 111 | if ( shift_sel[x] ) rd_data_shifted = rd_data_shifted_arr[x]; 112 | end 113 | end 114 | 115 | assign wr_fifo_shifted = wr_fifo_shifted_arr[seq_q[CNT_W-2:0]]; 116 | 117 | // buf data 118 | reg [FIFO_W-1:0] fifo_q; 119 | logic [FIFO_W-1:0] fifo_next; 120 | 121 | assign fifo_next = wr_fifo_shifted; 122 | always @(posedge clk) begin 123 | fifo_q <= fifo_next; 124 | end 125 | 126 | // reassemble output data 127 | logic [BLOCK_W-1:0] block; 128 | 129 | 130 | assign block = rd_data_mask & rd_data_shifted 131 | |~rd_data_mask & {2'bx, fifo_q}; 132 | 133 | assign valid_o = |seq_q[CNT_W-1:1]; // cnt_q > 1 134 | 135 | assign { data_o, head_o } = block; 136 | 137 | endmodule 138 | -------------------------------------------------------------------------------- /block_sync_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Per lane sync block, uses the sync header to 9 | * lock onto the the block. 10 | * 11 | * Funtionality outlined in 802.3 figure 49-14 12 | * 13 | * From clause 82 : 14 | * When the receive channel is in normal or test-pattern mode, the PCS Synchronization process continuously 15 | * monitors inst:IS_SIGNAL.indication(SIGNAL_OK). When SIGNAL_OK indicates OK, then the PCS 16 | * Synchronization process accepts data-units via the inst:IS_UNITDATA_i.indication primitive. It attains 17 | * block synchronization based on the 2-bit synchronization headers on each one of the PCS lanes. 18 | * 19 | * 20 | * */ 21 | module block_sync_rx#( 22 | parameter HEAD_W = 2 23 | )( 24 | input clk, 25 | input nreset, 26 | 27 | // SerDes 28 | input signal_v_i, // signal_ok 29 | 30 | // Gearbox 31 | input valid_i, // data valid 32 | input [HEAD_W-1:0] head_i, 33 | output slip_v_o, // slip_done 34 | 35 | // Status 36 | output lock_v_o // rx_block_lock 37 | 38 | ); 39 | localparam CNT_N = 1024; 40 | localparam CNT_W = $clog2(CNT_N); 41 | localparam NV_CNT_N = 65; 42 | localparam NV_CNT_W = $clog2(NV_CNT_N); 43 | 44 | /* fsm */ 45 | reg invalid_q; 46 | logic invalid_next; 47 | reg sync_q; // syncing in progress, havn't locked 48 | logic sync_next; 49 | reg lock_q; // have a valid lock 50 | logic lock_next; 51 | 52 | 53 | // sync header test 54 | logic sh_v; // sh_valid 55 | assign sh_v = head_i[0] ^ head_i[1]; // vaild syn header can be 2'b10 or 2'b01 56 | 57 | // counters 58 | logic [CNT_W-1:0] cnt_next; 59 | reg [CNT_W-1:0] cnt_q;// sh_cnt 60 | logic [CNT_W-1:0] cnt_add; 61 | logic cnt_add_overflow; // 1024 62 | logic cnt_64; 63 | logic cnt_1024; 64 | 65 | logic [NV_CNT_W-1:0] nv_cnt_next; 66 | reg [NV_CNT_W-1:0] nv_cnt_q;// sh_invalid_cnt 67 | logic [NV_CNT_W-1:0] nv_cnt_add; 68 | logic unused_nv_cnt_add_of; 69 | logic nv_cnt_65; 70 | 71 | logic cnt_rst_v; // reset counters ( RESET_CNT ) 72 | // lock set and unset 73 | logic lock_v; // 64_GOOD 74 | logic slip_v; // SLIP 75 | 76 | 77 | assign cnt_rst_v = invalid_q | lock_v | slip_v | cnt_1024; 78 | 79 | assign { cnt_add_overflow, cnt_add } = cnt_q + {{ CNT_W-1{1'b0}}, sh_v|lock_q }; 80 | assign { unused_nv_cnt_add_of, nv_cnt_add } = nv_cnt_q + {{NV_CNT_W-1{1'b0}}, ~sh_v }; 81 | 82 | assign cnt_next = cnt_rst_v ? {CNT_W{1'b0}} : cnt_add; 83 | assign nv_cnt_next = cnt_rst_v ? {NV_CNT_W{1'b0}} : nv_cnt_add; 84 | 85 | assign nv_cnt_65 = nv_cnt_add == 'd65; 86 | assign cnt_64 = cnt_add == 'd64; 87 | assign cnt_1024 = cnt_add_overflow; 88 | 89 | always @(posedge clk) begin 90 | if ( valid_i ) begin 91 | cnt_q <= cnt_next; 92 | nv_cnt_q <= nv_cnt_next; 93 | end 94 | end 95 | 96 | // lock and slip 97 | assign slip_v = sync_q & ~sh_v // TEST_SH -> SLIP 98 | | lock_q & nv_cnt_65; // INVALID_SH -> SLIP 99 | assign lock_v = sync_q & cnt_64; 100 | 101 | // fsm 102 | assign invalid_next = invalid_q & ~signal_v_i 103 | | ~signal_v_i; 104 | assign sync_next = signal_v_i & ( invalid_q // signal ok, start testing 105 | | sync_q & ~lock_v // continue testesing, not locked yet 106 | | lock_q & slip_v) ;// lost lock startup new sync process 107 | assign lock_next = signal_v_i 108 | & ( lock_q & ~slip_v // lock not lost 109 | | sync_q & lock_v); // locked 110 | 111 | always @(posedge clk) begin 112 | if ( ~nreset ) begin 113 | invalid_q <= 1'b1; 114 | sync_q <= 1'b0; 115 | lock_q <= 1'b0; 116 | end else if ( valid_i ) begin 117 | invalid_q <= invalid_next; 118 | sync_q <= sync_next; 119 | lock_q <= lock_next; 120 | end 121 | end 122 | 123 | // output 124 | assign lock_v_o = lock_q; 125 | assign slip_v_o = valid_i & slip_v; 126 | 127 | `ifdef FORMAL 128 | logic f_fsm; 129 | assign f_fsm = { invalid_q, sync_q, lock_q }; 130 | 131 | always @(posedge clk) begin 132 | if ( nreset ) begin 133 | // check fsm is onehot 134 | sva_fsm_onehot : assert( $onehot(f_fsm)); 135 | end 136 | end 137 | `endif 138 | endmodule 139 | -------------------------------------------------------------------------------- /tb/vpi/tb_utils.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "tb_utils.h" 9 | #include 10 | #include 11 | #include "defs.h" 12 | /* Note : Eventhough calloc set bits to 0 we are still manually 13 | * writing bval's to 0 for clarity */ 14 | 15 | void tb_vpi_put_logic_1b_t(vpiHandle h, uint8_t var){ 16 | 17 | s_vpi_value v; 18 | 19 | assert(h); 20 | v.format = vpiScalarVal; 21 | v.value.scalar = ( var )? vpi1 : vpi0; 22 | vpi_put_value(h, &v, 0, vpiNoDelay); 23 | } 24 | 25 | void tb_vpi_put_logic_uint8_t(vpiHandle h, uint8_t var){ 26 | 27 | s_vpi_value v; 28 | 29 | assert(h); 30 | v.format = vpiVectorVal; 31 | v.value.vector = calloc(1, sizeof(s_vpi_vecval)); 32 | v.value.vector[0].aval = (PLI_INT32) 0xffffff00 | (PLI_INT32)var; 33 | v.value.vector[0].bval = (PLI_INT32) 0xffffff00; 34 | vpi_put_value(h, &v, 0, vpiNoDelay); 35 | free(v.value.vector); 36 | } 37 | void tb_vpi_put_logic_uint32_t(vpiHandle h, uint32_t var){ 38 | 39 | s_vpi_value v; 40 | 41 | assert(h); 42 | 43 | info("tb_vpi_put_logic_uint32_t var %08x\n", var); 44 | 45 | v.format = vpiVectorVal; 46 | v.value.vector = calloc(1, sizeof(s_vpi_vecval)); 47 | v.value.vector[0].aval = (PLI_INT32)var; 48 | v.value.vector[0].bval = 0; 49 | vpi_put_value(h, &v, 0, vpiNoDelay); 50 | free(v.value.vector); 51 | } 52 | 53 | void tb_vpi_put_logic_uint64_t(vpiHandle h, uint64_t var){ 54 | 55 | s_vpi_value v; 56 | 57 | assert(h); 58 | v.format = vpiVectorVal; 59 | v.value.vector = calloc(2, sizeof(s_vpi_vecval)); 60 | v.value.vector[0].aval =(PLI_INT32) var; //32 lsb 61 | v.value.vector[0].bval = 0; 62 | v.value.vector[1].aval =(PLI_INT32) ( var >> 32 ); //32 msb 63 | v.value.vector[1].bval = 0; 64 | vpi_put_value(h, &v, 0, vpiNoDelay); 65 | free(v.value.vector); 66 | } 67 | 68 | void _tb_vpi_put_logic_char_var_arr(vpiHandle h, uint8_t *arr, size_t len){ 69 | size_t w_cnt; // word count, vpi vector val elems are only of 32b wide each 70 | size_t off; 71 | 72 | s_vpi_value v; 73 | 74 | assert(h); 75 | #ifdef DEBUG 76 | info("put_logic_char_var_arr len %ld data : ", len); 77 | for(int i=len-1; i>= 0; i--) 78 | info("%02x",arr[i]); 79 | info("\n"); 80 | #endif 81 | w_cnt = ((len*8)/ 32) + (((len*8) % 32 )? 1 :0 );// round to supperior 82 | v.format = vpiVectorVal; 83 | v.value.vector = calloc(w_cnt, sizeof(s_vpi_vecval)); 84 | for (size_t i = 0; i < w_cnt; i++){ 85 | v.value.vector[i].aval = 0; 86 | } 87 | 88 | for (size_t i = 0; i < w_cnt*4; i++){ 89 | off = (i%4)*8; 90 | if ( i < len ){ 91 | v.value.vector[i/4].aval |= (PLI_INT32) ( 0x000000ff & (uint32_t)arr[i] ) << off ; 92 | v.value.vector[i/4].bval |= ((PLI_INT32)0x00 ) << ((i%4)*8); 93 | }else{ 94 | v.value.vector[i/4].aval |= ((PLI_INT32)0xff) << ((i%4)*8); 95 | v.value.vector[i/4].bval |= ((PLI_INT32)0xff) << ((i%4)*8); 96 | } 97 | } 98 | vpi_put_value(h, &v, 0, vpiNoDelay); 99 | free(v.value.vector); 100 | } 101 | 102 | 103 | void tb_vpi_put_logic_uint64_t_var_arr(vpiHandle h, uint64_t *arr, size_t len){ 104 | size_t w_cnt; // word count, vpi vector val elems are only of 32b wide each 105 | size_t off; 106 | 107 | s_vpi_value v; 108 | 109 | assert(h); 110 | w_cnt = len*2; 111 | v.format = vpiVectorVal; 112 | v.value.vector = calloc(w_cnt, sizeof(s_vpi_vecval)); 113 | for (size_t i = 0; i < w_cnt; i++){ 114 | v.value.vector[i].aval = 0; 115 | } 116 | 117 | for (size_t i = 0; i < w_cnt; i++){ 118 | off = (i%2)*32; 119 | v.value.vector[i].aval = (PLI_INT32)(arr[i/2] >> off); 120 | v.value.vector[i].bval = (PLI_INT32)0x00 ; 121 | } 122 | vpi_put_value(h, &v, 0, vpiNoDelay); 123 | free(v.value.vector); 124 | } 125 | 126 | void tb_vpi_put_lite_logic_uint64_t_var_arr(vpiHandle h, uint64_t *arr, size_t len){ 127 | size_t w_cnt; // word count, vpi vector val elems are only of 32b wide each 128 | size_t off; 129 | s_vpi_value v; 130 | 131 | assert(h); 132 | w_cnt = len*2; 133 | v.format = vpiVectorVal; 134 | v.value.vector = calloc(w_cnt, sizeof(s_vpi_vecval)); 135 | for (size_t i = 0; i < w_cnt; i++){ 136 | v.value.vector[i].aval = 0; 137 | } 138 | 139 | for (size_t i = 0; i < w_cnt; i++){ 140 | off = (i%2)*32; 141 | v.value.vector[i].aval = (PLI_INT32)(arr[i/2] >> off); 142 | v.value.vector[i].bval = (PLI_INT32)0x00 ; 143 | } 144 | vpi_put_value(h, &v, 0, vpiNoDelay); 145 | free(v.value.vector); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /tb/vpi/makefile: -------------------------------------------------------------------------------- 1 | 2 | ################ 3 | # Sanitization # 4 | ################ 5 | 6 | # Disable builtin implicit rules. 7 | .SUFFIXES : 8 | % :: %,v 9 | % :: s.% 10 | % :: RCS/%,v 11 | % :: RCS/% 12 | % :: SCCS/% 13 | % :: SCCS/s.% 14 | 15 | ############ 16 | # Sim type # 17 | ############ 18 | 19 | # Define simulator we are using, priority to iverilog 20 | SIM ?= I 21 | $(info Using simulator: $(SIM)) 22 | 23 | ########### 24 | # Globals # 25 | ########### 26 | 27 | # AUR Path. 28 | AUR := $(HOME)/AUR 29 | 30 | # Compilation flags. 31 | CC_FLAGS := -Wall -Wextra -Wconversion -Wshadow -Wundef -fno-common -Wno-unused-parameter -Wno-type-limits -fpic 32 | CC_FLAGS += $(if $(debug),-g) 33 | 34 | # Compilation defines. 35 | CC_DEFS := $(if $(debug),-DDEBUG) 36 | CC_DEFS +=$(if $(40GBASE), -D_40GBASE) 37 | 38 | # Link flags. 39 | LD_FLAGS := $(if $(debug),-g) 40 | 41 | # Per-testbench parameters. 42 | ifeq ($(SIM),I) 43 | BUILD=build 44 | CC_CMD=cc 45 | LD_CMD=cc 46 | CC_FLAGS += -std=gnu99 47 | 48 | # set vpi lib path when using iverilog : 49 | VPI_INC := -I$(AUR)/iverilog 50 | 51 | VPI_DIR := vpi_i 52 | PP := 53 | else 54 | BUILD=obj_vpi 55 | CC_CMD=g++ 56 | LD_CMD=g++ 57 | CC_FLAGS += -fpermissive 58 | CC_DEFS += -DVERILATOR 59 | CC_DEFS += $(if $(wave),-DVM_TRACE) 60 | 61 | # set vpi lib path when using verilator : 62 | VPI_INC := -I/usr/local/share/verilator/include 63 | VPI_INC += -I/usr/local/share/verilator/include/vltstd 64 | 65 | VPI_INC += -I../../obj_dir 66 | VPI_DIR := vpi_v 67 | PP := pp 68 | endif 69 | 70 | # Log. 71 | $(info Using SIM=$(SIM), building to $(BUILD) dir) 72 | 73 | # Compilation utils. 74 | CC := $(CC_CMD) $(CC_FLAGS) $(CC_DEFS) 75 | LD := $(LD_CMD) $(LD_FLAGS) 76 | 77 | ########### 78 | # Headers # 79 | ########### 80 | 81 | # All headers that are accessible to .c/cpp files. 82 | # Any change in those will cause any required .o file to be rebuilt. 83 | HDRS := pcs_gearbox.h pcs_defs.h pcs_enc.h pcs_marker.h pcs_tx.h 84 | HDRS += 64b66b.h 85 | HDRS += tb_rand.h tb_fifo.h tb_utils.h 86 | HDRS += tb_pcs_common.h tb_marker_common.h 87 | HDRS += tv.h 88 | HDRS += $(VPI_DIR)/tb_marker.h$(PP) 89 | HDRS += $(VPI_DIR)/tb_pcs.h$(PP) 90 | 91 | ######### 92 | # Build # 93 | ######### 94 | 95 | # General sources 96 | SRCS := test 97 | SRCS += pcs_gearbox pcs_enc pcs_marker pcs_tx 98 | SRCS += tb_rand tb_fifo tb_utils 99 | SRCS += tb_marker_common tb_pcs_common 100 | SRCS += 64b66b 101 | SRCS += tv 102 | 103 | # VPI sources 104 | VPI_SRCS := tb_marker 105 | VPI_SRCS += tb_pcs 106 | 107 | # Standard object file build recipe. 108 | define obj_recipe 109 | $$(BUILD)/$1.o:$1.c $(HDRS) 110 | @mkdir -p $$(@D) 111 | $$(CC) $$(VPI_INC) -o $$@ -c $1.c 112 | 113 | endef 114 | 115 | # VPI object file build recipe. 116 | define vpi_obj_recipe 117 | $$(BUILD)/$1.o: $$(VPI_DIR)/$1.c$(PP) $$(HDRS) 118 | @mkdir -p $$(@D) 119 | $$(CC) $$(VPI_INC) -o $$@ -c $$(VPI_DIR)/$1.c$$(PP) 120 | 121 | endef 122 | 123 | # Generate run recipes for each testbench. 124 | $(eval $(foreach x,$(SRCS),$(call obj_recipe,$x))) 125 | $(eval $(foreach x,$(VPI_SRCS),$(call vpi_obj_recipe,$x))) 126 | 127 | ################ 128 | # Dependencies # 129 | ################ 130 | 131 | # Dependencies for all tests. '.o'-s ommitted. 132 | 40g_deps := $(if $(40GBASE),pcs_marker) 133 | deps_all := tv pcs_gearbox pcs_enc 64b66b pcs_tx tb_fifo tb_rand $(40g_deps) 134 | 135 | # Test dependencies names. '.o'-s ommitted. 136 | test_deps_names := test $(deps_all) 137 | tb_deps_names := tb_pcs tb_pcs_common tb_utils $(deps_all) 138 | tb_marker_deps_names := tb_marker tb_utils tb_marker_common tb_rand pcs_marker 139 | 140 | # Dependencies generator. 141 | gen_deps = $(foreach x,$($(1)_deps_names),$(BUILD)/$x.o) 142 | 143 | # Dependencies. 144 | test_deps := $(call gen_deps,test) 145 | tb_deps := $(call gen_deps,tb) 146 | tb_marker_deps := $(call gen_deps,tb_marker) 147 | 148 | ########### 149 | # Targets # 150 | ########### 151 | 152 | test: $(test_deps) 153 | $(LD) -o test -g $^ 154 | 155 | $(BUILD)/tb_all.o: $(tb_deps) 156 | @mkdir -p $(@D) 157 | $(LD) -r -o $(BUILD)/tb_all.o $^ 158 | 159 | $(BUILD)/tb.vpi: $(BUILD)/tb_all.o 160 | @mkdir -p $(@D) 161 | $(LD) -shared -o $(BUILD)/tb.vpi $^ -lvpi 162 | 163 | $(BUILD)/tb_marker_all.o: $(tb_marker_deps) 164 | @mkdir -p $(@D) 165 | $(LD) -r -o $(BUILD)/tb_marker_all.o $^ 166 | 167 | $(BUILD)/tb_marker.vpi: $(BUILD)/tb_marker_all.o 168 | @mkdir -p $(@D) 169 | $(LD) -shared -o $(BUILD)/tb_marker.vpi $^ -lvpi 170 | 171 | #################### 172 | # Standard targets # 173 | #################### 174 | 175 | clean: 176 | rm -rf build/* 177 | rm -rf obj_vpi/* 178 | rm -f test 179 | rm -f vgcore.* 180 | 181 | -------------------------------------------------------------------------------- /tb/block_sync_rx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `define SYNC_CTRL 2'b10 9 | `define SYNC_DATA 2'b01 10 | 11 | `define TB_TEST3_LOOP 150 12 | `define TB_TEST6_LOOP 10 13 | 14 | /* Testbench for RX frame sync */ 15 | module block_sync_rx_tb; 16 | 17 | localparam HEAD_W = 2; 18 | localparam DATA_W = 64; 19 | localparam BLOCK_W = HEAD_W + DATA_W; 20 | 21 | reg clk = 1'b0; 22 | logic nreset; 23 | logic valid_i; // data valid 24 | logic signal_v_i; // signal_ok 25 | logic [HEAD_W-1:0] head_i; 26 | logic slip_v_o; // slip_done 27 | logic lock_v_o; // rx_block_lock 28 | 29 | always clk = #5 ~clk; 30 | 31 | int loop_lock; 32 | int nv_sh_sent; 33 | int sh_v; 34 | 35 | task aquire_lock( input int loop_lock ); 36 | // need at least 64 valid blocks to aquire lock 37 | assert( loop_lock >= 64 ); 38 | for(int t=0; t < loop_lock; t++ ) begin 39 | #9 40 | head_i = ( $random % 2 )? `SYNC_CTRL : `SYNC_DATA; 41 | 42 | /* send invalid signal, do no count these cycles */ 43 | valid_i = (( $random % 6 ) == 0 )? 1'b0: 1'b1; 44 | if ( valid_i == 1'b0 ) begin 45 | t--; 46 | end 47 | 48 | signal_v_i = 1'b1; 49 | // only sending valid lock's should never slip 50 | #1 51 | assert( ~slip_v_o ); 52 | end 53 | endtask 54 | 55 | initial begin 56 | $dumpfile("wave/block_sync_rx.vcd"); 57 | $dumpvars(0, block_sync_rx_tb); 58 | nreset = 1'b0; 59 | #10; 60 | nreset = 1'b1; 61 | valid_i = 1'b1; 62 | signal_v_i = 1'b0; 63 | // test 1 : TEST_SH -> 64_GOOD -> TEST_SH2 64 | // simple test, see if we can detect lock 65 | // we need at least 64 blocks to trigger a lock 66 | $display("test 1 %t", $time); 67 | loop_lock = 64 + ( $random % 20 ); 68 | aquire_lock(loop_lock); 69 | // we should be locked 70 | assert( lock_v_o ); 71 | 72 | // test 2 : TEST_SH2 -> INVALID_SH -> SLIP 73 | // After test 1 we have a lock, we will start 74 | // sending some random sync headers and our logic 75 | // should detect this and trigger the slip 76 | // A slip should be detected after 65 wrong heads, but 77 | // random data might contain a valid sync header ( x3 or x1 ) 78 | $display("test 2 %t", $time); 79 | nv_sh_sent = 0; 80 | do begin 81 | #10 82 | head_i = $random; 83 | // check if head is valid 84 | nv_sh_sent = ( (head_i == `SYNC_DATA) || (head_i == `SYNC_CTRL) ) ? nv_sh_sent: nv_sh_sent+1; 85 | end while (nv_sh_sent != 65); 86 | #1 87 | assert( slip_v_o ); 88 | #9 89 | // check if we lost the lock 90 | assert( ~lock_v_o); 91 | 92 | // test 3 : TEST_SH -> SLIP 93 | // We have lost the lock at this point. 94 | // Sending rublish data for a few cycles and 95 | // check we do not lock and are slipining on 96 | // every invalid head 97 | $display("test 3 %t", $time); 98 | sh_v = 0; 99 | for(int t=0; t < `TB_TEST3_LOOP; t++) begin 100 | #9 101 | head_i = $random; 102 | // check if head is valid 103 | sh_v = ( (head_i == `SYNC_DATA) || (head_i == `SYNC_CTRL) ) ? 1:0; 104 | #1 105 | if ( !sh_v ) begin 106 | assert( slip_v_o ); 107 | end 108 | end 109 | 110 | // test 4 111 | // Re-aquire lock and once we have re-locked 112 | // lose the signal. 113 | $display("test 4 %t", $time); 114 | aquire_lock(loop_lock); 115 | assert(lock_v_o); 116 | #10; 117 | valid_i = 1'b1; 118 | signal_v_i = 1'b0; 119 | // continue sending valid headers but should have lost lock and there 120 | // should be no slip 121 | for(int t = 0; t < loop_lock; t++) begin 122 | #9 123 | head_i = ( $random % 2 )? `SYNC_CTRL : `SYNC_DATA; 124 | #1 125 | assert( ~slip_v_o ); 126 | assert( ~lock_v_o ); 127 | end 128 | // test 5 129 | // Re-establish signal, re-aquire lock 130 | $display("test 5 %t", $time); 131 | signal_v_i = 1'b1; 132 | aquire_lock( loop_lock ); 133 | assert( lock_v_o ); 134 | 135 | /* test 6 136 | * Sending invalid data, with random data, 137 | * block lock state should not change */ 138 | $display("test 6 %t", $time); 139 | valid_i = 1'b0; 140 | for(int t=0; t< `TB_TEST6_LOOP; t++) begin 141 | #9 142 | head_i = $random; 143 | #1 144 | /* by default we have lock, we should still 145 | * have the lock */ 146 | assert(lock_v_o); 147 | end 148 | valid_i = 1'b1; 149 | 150 | $display("Test finished %t", $time); 151 | signal_v_i = 1'b0; 152 | #20 153 | $finish; 154 | end 155 | 156 | block_sync_rx #(.HEAD_W(HEAD_W)) 157 | m_uut( 158 | .clk(clk), 159 | .nreset(nreset), 160 | .signal_v_i(signal_v_i), 161 | .valid_i(valid_i), 162 | .head_i(head_i), 163 | .slip_v_o(slip_v_o), 164 | .lock_v_o(lock_v_o) 165 | ); 166 | 167 | 168 | endmodule 169 | -------------------------------------------------------------------------------- /tb/gearbox_rx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* RX gearbox tb, test basis 64 -> 66 buffering functionality and bit slip */ 9 | 10 | `define TB_LOOP_CNT 10 11 | 12 | module gearbox_rx_tb; 13 | 14 | localparam DATA_W = 64; 15 | localparam HEAD_W = 2; 16 | localparam SEQ_N = 32; 17 | localparam BLOCK_W = DATA_W + HEAD_W; 18 | 19 | reg clk = 1'b0; 20 | 21 | /* verilator lint_off BLKSEQ */ 22 | always clk = #5 ~clk; 23 | /* verilator lint_on BLKSEQ */ 24 | logic nreset; 25 | logic lock_v_i; 26 | logic [DATA_W-1:0] data_i; 27 | logic slip_v_i; 28 | logic valid_o; // backpressure, buffer is full, need a cycle to clear 29 | logic [HEAD_W-1:0] head_o; 30 | logic [DATA_W-1:0] data_o; 31 | 32 | /* tb 33 | * full data to be received after gearbox */ 34 | logic [DATA_W-1:0] tb_pma_data; 35 | 36 | logic [BLOCK_W-1:0] tb_pcs_data; 37 | logic [BLOCK_W-1:0] tb_pcs_data_arr[SEQ_N:0]; 38 | 39 | /* verilator lint_off UNUSEDSIGNAL */ 40 | /* debug signal to view the difference between gotten 41 | * and expected */ 42 | logic [BLOCK_W-1:0] tb_pcs_data_diff; 43 | /* verilator lint_on UNUSEDSIGNAL */ 44 | 45 | reg [DATA_W-1:0] tb_buff_q; 46 | logic [DATA_W-1:0] tb_buff_next_arr[SEQ_N:0]; 47 | logic [DATA_W-1:0] tb_buff_next; 48 | 49 | logic [BLOCK_W-1:0] tb_gb_data; 50 | 51 | /* Simple gearbox test 52 | * There is no slipage during this sequence. 53 | * This test generate a random array of 66b of data for a given 54 | * sequence and check it is correctly outputed aligned on 64b */ 55 | task simple_test(); 56 | // 64 bit pma data 57 | logic [DATA_W-1:0] pma; 58 | 59 | for( int seq = 0; seq<= SEQ_N; seq++ ) begin 60 | lock_v_i = 1'b1; 61 | slip_v_i = 1'b0; 62 | //pma = {8{8'h01}}; 63 | pma = {$random, $random}; 64 | tb_pma_data = pma; 65 | 66 | data_i = tb_pma_data; 67 | #1 68 | 69 | tb_buff_next = tb_buff_next_arr[seq]; 70 | tb_pcs_data = tb_pcs_data_arr[seq]; 71 | 72 | /* check pcs data matches */ 73 | tb_gb_data = { data_o, head_o }; 74 | if ( seq > 0 ) begin 75 | tb_pcs_data_diff = tb_gb_data ^ tb_pcs_data; 76 | assert( tb_gb_data == tb_pcs_data); 77 | end 78 | #9 79 | lock_v_i = 1'b1; 80 | end 81 | endtask 82 | 83 | genvar i; 84 | generate 85 | for( i = 0; i < SEQ_N; i++) begin : tb_buff_next_loop 86 | if ( i == 0 ) begin : i_eq_zero 87 | assign tb_buff_next_arr[i] = tb_pma_data[DATA_W-1:0]; 88 | assign tb_pcs_data_arr[i] = {BLOCK_W{1'bx}}; 89 | end else begin : i_gt_zero 90 | assign tb_buff_next_arr[i] ={ {2*i{1'bx}}, tb_pma_data[DATA_W-1:2*i]}; 91 | assign tb_pcs_data_arr[i] = { tb_pma_data[2*i-1:0], tb_buff_q[DATA_W-1-2*(i-1):0]}; 92 | end 93 | `ifdef DEBUG 94 | logic [BLOCK_W-1:0] db_pcs_data; 95 | logic [DATA_W-1:0] db_buff_next; 96 | assign db_pcs_data = tb_pcs_data_arr[i]; 97 | assign db_buff_next = tb_buff_next_arr[i]; 98 | `endif 99 | 100 | end 101 | endgenerate 102 | /* buffer tb data */ 103 | always @(posedge clk) begin 104 | tb_buff_q <= tb_buff_next; 105 | end 106 | 107 | /* Bit Slip test */ 108 | task slip(); 109 | lock_v_i = 1'b1; 110 | data_i = {8{8'h01}}; 111 | for(int x=0; x < 64; x++) begin 112 | #10 113 | /* verilator lint_off WIDTHTRUNC*/ 114 | slip_v_i = $random; 115 | /* verilator lint_on WIDTHTRUNC*/ 116 | end 117 | slip_v_i = 1'b0; 118 | endtask 119 | 120 | /* Lock lost test 121 | * Check to see if gearbox output is correctly invalid */ 122 | task lock_lost(); 123 | lock_v_i = 1'b0; 124 | for(int x=0; x < `TB_LOOP_CNT; x++) begin 125 | #10 126 | assert( valid_o == 1'b0 ); 127 | /* check sequence cnt is re-set to 0 */ 128 | assert( m_gearbox_rx.seq_q == 'd0 ); 129 | end 130 | lock_v_i = 1'b1; 131 | endtask 132 | 133 | /* data check */ 134 | always @(posedge clk) begin 135 | if ( nreset ) begin 136 | assert( ~$isunknown(valid_o)); 137 | if ( valid_o ) begin 138 | assert( ~$isunknown(data_o)); 139 | assert( ~$isunknown(head_o)); 140 | end 141 | end 142 | end 143 | 144 | initial begin 145 | $dumpfile("wave/gearbox_rx_tb.vcd"); 146 | $dumpvars(0, gearbox_rx_tb); 147 | nreset = 1'b0; 148 | lock_v_i = 1'b0; 149 | #10 150 | nreset = 1'b1; 151 | 152 | /* Test 1 */ 153 | $display("test 1 %t", $time); 154 | simple_test(); 155 | 156 | /* Test 2 */ 157 | $display("test 2 %t", $time); 158 | /* test slipage */ 159 | slip(); 160 | #10 161 | 162 | /* Test 3 */ 163 | $display("test 3 %t", $time); 164 | /* test lock lost */ 165 | lock_lost(); 166 | 167 | // self check 168 | // no difference between got and expected 169 | #10 170 | $display("Sucess"); 171 | $finish; 172 | end 173 | 174 | // uut 175 | gearbox_rx #( 176 | .HEAD_W(HEAD_W), 177 | .DATA_W(DATA_W) 178 | )m_gearbox_rx( 179 | .clk(clk), 180 | .nreset(nreset), 181 | .lock_v_i(lock_v_i), 182 | .data_i(data_i), 183 | .slip_v_i(slip_v_i), 184 | .valid_o(valid_o), 185 | .head_o(head_o), 186 | .data_o(data_o) 187 | ); 188 | endmodule 189 | -------------------------------------------------------------------------------- /dec_lite_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* PCS decode block, use on rx path. 9 | * Received block data may be invalid if it is 10 | * deemed malformed. 11 | */ 12 | module dec_lite_rx #( 13 | parameter IS_40G = 0, 14 | parameter HEAD_W = 2, 15 | parameter DATA_W = 64, 16 | parameter KEEP_W = DATA_W/8, 17 | parameter LANE0_CNT_N = IS_40G ? 1 : 2, 18 | parameter BLOCK_TYPE_W = 8 19 | )( 20 | input [HEAD_W-1:0] head_i, 21 | input [DATA_W-1:0] data_i, 22 | 23 | // lite dec interface, not x(l)gmii interface 24 | output ctrl_v_o, 25 | output idle_v_o, 26 | output [LANE0_CNT_N-1:0] start_v_o, 27 | output term_v_o, 28 | output err_v_o, 29 | output ord_v_o, 30 | output [DATA_W-1:0] data_o, // x(l)gmii data 31 | output [KEEP_W-1:0] keep_o 32 | ); 33 | /* verilator lint_off UNUSEDPARAM*/ 34 | localparam [BLOCK_TYPE_W-1:0] 35 | BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT 36 | BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT 37 | BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT 38 | BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT 39 | BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT 40 | BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT 41 | BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT 42 | BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT 43 | BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT 44 | BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT 45 | BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT 46 | BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT 47 | BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT 48 | BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT 49 | BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT 50 | 51 | localparam SYNC_HEAD_CTRL = 2'b10; 52 | localparam SYNC_HEAD_DATA = 2'b01; 53 | 54 | /* verilator lint_on UNUSEDPARAM*/ 55 | 56 | // recieved block data is invalid, it is malformed. 57 | // follows rules outlined in 49.2.4.6 58 | logic block_nv; 59 | 60 | // head 61 | logic head_v; // head is wellformed 62 | assign head_v = head_i[0] ^ head_i[1]; 63 | 64 | 65 | // look for matching control code 66 | logic [BLOCK_TYPE_W-1:0] block_type; 67 | logic block_type_none; 68 | logic idle_lite; 69 | logic ord_lite; // ordered set 70 | logic [KEEP_W-1:0] term_lite; 71 | logic [LANE0_CNT_N-1:0] start_lite; 72 | logic idle_v; 73 | logic err_v; 74 | logic ord_v; // ordered set 75 | logic [KEEP_W-1:0] term_v; 76 | logic [LANE0_CNT_N-1:0] start_v; 77 | 78 | assign block_type = data_i[BLOCK_TYPE_W-1:0]; 79 | 80 | assign idle_lite = block_type == BLOCK_TYPE_CTRL; 81 | assign ord_lite = 1'b0; // TODO : add support for order set codes 82 | 83 | assign term_lite[0] = block_type == BLOCK_TYPE_TERM_0; 84 | assign term_lite[1] = block_type == BLOCK_TYPE_TERM_1; 85 | assign term_lite[2] = block_type == BLOCK_TYPE_TERM_2; 86 | assign term_lite[3] = block_type == BLOCK_TYPE_TERM_3; 87 | assign term_lite[4] = block_type == BLOCK_TYPE_TERM_4; 88 | assign term_lite[5] = block_type == BLOCK_TYPE_TERM_5; 89 | assign term_lite[6] = block_type == BLOCK_TYPE_TERM_6; 90 | assign term_lite[7] = block_type == BLOCK_TYPE_TERM_7; 91 | 92 | assign start_lite[0] = block_type == BLOCK_TYPE_START_0; 93 | 94 | generate 95 | if ( !IS_40G ) begin : gen_is_10g 96 | assign start_lite[1] = block_type == BLOCK_TYPE_START_4; 97 | end 98 | endgenerate 99 | 100 | // no valid control code was dound 101 | assign block_type_none = ~( idle_lite | ord_lite | |term_lite | |start_lite ); 102 | 103 | // mask control code if the block is invalid 104 | assign err_v = block_nv; 105 | assign idle_v = idle_lite & ~block_nv; 106 | assign ord_v = ord_lite & ~block_nv; 107 | assign term_v = term_lite & {KEEP_W{~block_nv}}; 108 | assign start_v = start_lite & {LANE0_CNT_N{~block_nv}}; 109 | 110 | // keep signal value 111 | // this signal exists only in our implementation, as such it doesn't follow 802.3 112 | // as we will not be using start_4 our keep signal doesn't show a correct mask for 113 | // that case ( 8'b1110_0000 ). 114 | // keep signal is only valid for term 115 | assign keep_o = term_lite - {{KEEP_W-1{1'b0}}, 1'b1}; 116 | 117 | // check if block is valid 118 | assign block_nv = ~head_v | ( head_v & head_i[1] & block_type_none); 119 | 120 | // output 121 | // check if we have ctrl, or reception error 122 | assign ctrl_v_o = head_i[1] | block_nv; 123 | 124 | assign idle_v_o = idle_v; 125 | assign err_v_o = err_v; 126 | assign ord_v_o = ord_v; 127 | assign term_v_o = |term_v; 128 | assign start_v_o = start_v; 129 | 130 | assign data_o = data_i; 131 | 132 | `ifdef FORMAL 133 | `endif 134 | endmodule 135 | -------------------------------------------------------------------------------- /tb/vpi/pcs_marker.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include "pcs_marker.h" 9 | #include 10 | #include "defs.h" 11 | /* See ieee 802.3 clause 82.4 12 | * 13 | * Table 82–4—BIP 3 bit assignments 14 | * BIP_3 bit number Assigned 66-bit word bits 15 | * 0 2, 10, 18, 26, 34, 42, 50, 58 16 | * 1 3, 11, 19, 27, 35, 43, 51, 59 17 | * 2 4, 12, 20, 28, 36, 44, 52, 60 18 | * 3 0, 5, 13, 21, 29, 37, 45, 53, 61 19 | * 4 1, 6, 14, 22, 30, 38, 46, 54, 62 20 | * 5 7, 15, 23, 31, 39, 47, 55, 63 21 | * 6 8, 16, 24, 32, 40, 48, 56, 64 22 | * 7 9, 17, 25, 33, 41, 49, 57, 65 23 | */ 24 | 25 | #define BVAL(i) (uint8_t)( i < 2 ? (( head >> i ) & 1u) : ( data >> (i-2) & 1u )) 26 | #define BIP(x,i) (uint8_t) (x & (0x1<data = 0; 111 | for(uint8_t i=0; i<8; i++) 112 | out->data |= (uint64_t)mark_arr[lane][i] << i*8; 113 | out->head = SYNC_HEAD_CTRL; 114 | info("marker lane %d { %lx, %x } bip3 %x bip7 %x\n",lane, out->data, out->head, bip3, bip7); 115 | } 116 | 117 | bool alignement_marker( 118 | marker_s *state, 119 | const size_t lane, 120 | const block_s in, 121 | block_s *out) 122 | { 123 | bool need_marker; 124 | info("bip3 %x gap %d\n", state->bip[lane], state->gap); 125 | // check if we need to add marker 126 | need_marker = is_alignement_marker(*state); 127 | if ( need_marker ){ 128 | // add alignement data 129 | _create_alignement_marker(lane, state->bip[lane], out); 130 | // reset gap counter and bip 131 | state->bip[lane] = 0; 132 | if( lane == LANE_N-1 ){ 133 | state->gap = 0; 134 | } 135 | }else{ 136 | memcpy(out, &in, sizeof(block_s)); 137 | if ( lane == LANE_N-1) state->gap ++; 138 | } 139 | // continue calculating bip value 140 | state->bip[lane] = calculate_bip_per_lane(state->bip[lane], *out); 141 | // update gap on last lane 142 | 143 | return need_marker; 144 | } 145 | 146 | 147 | -------------------------------------------------------------------------------- /tb/vpi/tv.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "tv.h" 13 | #include "tb_rand.h" 14 | #include "defs.h" 15 | 16 | tv_t *tv_alloc(){ 17 | tv_t *tv; 18 | info("tv alloc"); 19 | tv = (tv_t *) malloc( sizeof(tv_t)); 20 | // init fifo 21 | for( size_t l = 0; l < LANE_N; l ++ ){ 22 | tv->block[l] = tb_data_fifo_ctor(); 23 | tv->data[l] = tb_data_fifo_ctor(); 24 | } 25 | // init values 26 | tv->len = 0; 27 | //tv->rd_idx = 0; 28 | //tv->packet = NULL; 29 | tv->idle_cntdown = 0; 30 | tv->debug_id = 0; 31 | tv->tx = pcs_tx_init(); 32 | return tv; 33 | } 34 | size_t add_to_fifo( 35 | tv_t *t, 36 | const bool accept, 37 | size_t idx, 38 | block_s *data, 39 | ctrl_lite_s *ctrl, 40 | block_s *block 41 | ){ 42 | size_t lane_idx = IDX_TO_LANE(idx++); 43 | t->debug_id ++; 44 | info("\n\ndata ptr %016lx, lane %ld\n", 45 | (int64_t) data, lane_idx); 46 | if(accept) { 47 | tb_data_fifo_push( 48 | t->data[lane_idx], 49 | data, 50 | ctrl, 51 | t->debug_id); 52 | } 53 | tb_data_fifo_push( 54 | t->block[lane_idx], 55 | block, 56 | NULL, 57 | t->debug_id); 58 | return idx; 59 | } 60 | 61 | void tv_create_packet(tv_t *t, const int start_lane ){ 62 | long int idle; 63 | ctrl_lite_s *ctrl; 64 | size_t idx; 65 | uint8_t *tmp_data; 66 | size_t data_idx = 0; 67 | bool accept; 68 | 69 | info("create packet\n"); 70 | 71 | t->idle_cntdown = tb_rand_packet_idle_cntdown(); 72 | 73 | idx = (size_t) start_lane; 74 | // fill packet with real random data 75 | t->len = tb_rand_get_packet_len(); 76 | tmp_data = (uint8_t*) malloc(sizeof(uint8_t)*t->len); 77 | tb_rand_fill_packet(tmp_data, t->len); 78 | 79 | // create expected result 80 | memset(&ctrl, 0, sizeof(ctrl_lite_s)); 81 | idle = (long int) t->idle_cntdown; 82 | 83 | do{ 84 | // idle frames 85 | ctrl = malloc( sizeof(ctrl_lite_s)); 86 | memset(ctrl, 0, sizeof(ctrl_lite_s)); 87 | ctrl->ctrl_v = 1; 88 | ctrl->idle_v = 1; 89 | block_s *data = malloc(sizeof(block_s)); 90 | block_s *exp = malloc(sizeof(block_s)); 91 | data->data = 0; 92 | 93 | accept = get_next_exp(t->tx, *ctrl, data, exp); 94 | if (accept) idle--; 95 | info("Idle %ld accept %d\n", idle,accept); 96 | // add to fifo 97 | idx = add_to_fifo(t, accept, idx, data, ctrl, exp ); 98 | }while(idle); 99 | // start 100 | { 101 | ctrl = malloc(sizeof(ctrl_lite_s)); 102 | memset(ctrl, 0, sizeof(ctrl_lite_s)); 103 | ctrl->ctrl_v = 1; 104 | ctrl->start_v[0] = 1; 105 | block_s *data = malloc(sizeof(ctrl_lite_s)); 106 | data->data = 0; 107 | for(size_t i=1; i< TXD_W; i++, data_idx++){ 108 | data->data |= (uint64_t) tmp_data[data_idx] << i*8 ; 109 | } 110 | do { 111 | block_s *exp = malloc(sizeof(block_s)); 112 | accept = get_next_exp(t->tx, *ctrl, data, exp); 113 | idx = add_to_fifo(t, accept, idx, data, ctrl, exp); 114 | } while (!accept); 115 | } 116 | 117 | while( t->len - data_idx > (TXD_W-1)){ 118 | ctrl = malloc(sizeof(ctrl_lite_s)); 119 | memset(ctrl, 0, sizeof(ctrl_lite_s)); 120 | block_s *data = malloc(sizeof(block_s)); 121 | data->data = 0; 122 | for(size_t i=0; i< TXD_W; i++){ 123 | data->data = data->data | (((uint64_t) tmp_data[data_idx+i])<< i*8 ); 124 | } 125 | block_s *exp = malloc(sizeof(block_s)); 126 | accept = get_next_exp(t->tx, *ctrl, data, exp); 127 | idx = add_to_fifo(t, accept, idx, data, ctrl, exp); 128 | if (accept){ 129 | data_idx += TXD_W; 130 | info("idx %ld\n", idx); 131 | }else{ 132 | info("data rejected idx %ld\n", idx); 133 | } 134 | } 135 | // terminate 136 | { 137 | ctrl = malloc(sizeof(ctrl_lite_s)); 138 | memset(ctrl, 0, sizeof(ctrl_lite_s)); 139 | ctrl->ctrl_v = 1; 140 | ctrl->term_v = 1; 141 | LEN_TO_KEEP((t->len-data_idx), ctrl->term_keep); 142 | block_s *data = malloc(sizeof(block_s)); 143 | data->data = 0; 144 | for(size_t i=1; i< TXD_W && data_idx < t->len; i++, data_idx++){ 145 | data->data = data->data | (((uint64_t) tmp_data[data_idx])<< i*8 ); 146 | } 147 | 148 | do { 149 | block_s *exp = malloc(sizeof(block_s)); 150 | accept = get_next_exp(t->tx, *ctrl, data, exp); 151 | idx = add_to_fifo(t, accept, idx, data, ctrl, exp); 152 | } while (!accept); 153 | } 154 | #ifdef DEBUG 155 | info("FIFO\ndata \n"); 156 | for(size_t l=0; ldata[l]); 158 | info("exp \n"); 159 | for(size_t l=0; lblock[l]); 161 | #endif 162 | free(tmp_data); 163 | } 164 | 165 | bool tv_get_next_txd( 166 | tv_t *t, 167 | ctrl_lite_s **ctrlp, 168 | block_s **datap, 169 | uint64_t *debug_idp, 170 | const int lane 171 | ) 172 | { 173 | assert(TXD_W < 9 );// not supported yet 174 | block_s *data = *datap = tb_data_fifo_pop(t->data[lane], debug_idp, ctrlp); 175 | if (data != NULL){ 176 | info("data : %016lx debug_id %016lx\n", data->data, *debug_idp); 177 | return true; 178 | } else{ 179 | return false; 180 | } 181 | } 182 | 183 | 184 | // check if we still have data to send 185 | /*bool tv_txd_has_data(tv_t *t, const int lane){ 186 | return ; 187 | }*/ 188 | 189 | void tv_free(tv_t *t) 190 | { 191 | for(int i=0; i< LANE_N; i++){ 192 | tb_data_fifo_dtor(t->data[i]); 193 | tb_data_fifo_dtor(t->block[i]); 194 | } 195 | free(t->tx); 196 | //free(t->packet); 197 | free(t); 198 | } 199 | -------------------------------------------------------------------------------- /am_lock_rx.v: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | /* Alignement marker lock */ 9 | module am_lock_rx #( 10 | parameter BLOCK_W = 66, 11 | parameter LANE_N = 4 12 | )( 13 | input clk, 14 | input nreset, 15 | 16 | /* CDC */ 17 | input valid_i, 18 | 19 | /* verilator lint_off UNUSEDSIGNAL*/ 20 | // block data, bip3 and bip7 will not be checked 21 | input [BLOCK_W-1:0] block_i, 22 | /* verilator lint_on UNUSEDSIGNAL*/ 23 | 24 | output lock_v_o, 25 | 26 | /* Deskew */ 27 | /* found an match with an alignement marker */ 28 | output lite_am_v_o, 29 | /* early lock interface, we have seen an alignement marker */ 30 | output lite_lock_v_o, 31 | 32 | output [LANE_N-1:0] lane_o 33 | ); 34 | localparam GAP_N = 16383; 35 | localparam GAP_W = $clog2(GAP_N); 36 | localparam NV_CNT_N = 4; 37 | localparam NV_CNT_W = $clog2(NV_CNT_N); 38 | localparam SYNC_CTRL = 2'b10; 39 | localparam [BLOCK_W-1:0] 40 | MARKER_LANE0 = { {8{1'bx}},8'hb8, 8'h89, 8'h6f, {8{1'bx}}, 8'h47, 8'h76, 8'h90 ,SYNC_CTRL}, 41 | MARKER_LANE1 = { {8{1'bx}},8'h19, 8'h3b, 8'h0f, {8{1'bx}}, 8'he6, 8'hc4, 8'hf0 ,SYNC_CTRL}, 42 | MARKER_LANE2 = { {8{1'bx}},8'h64, 8'h9a, 8'h3a, {8{1'bx}}, 8'h9b, 8'h65, 8'hc5 ,SYNC_CTRL}, 43 | MARKER_LANE3 = { {8{1'bx}},8'hc2, 8'h86, 8'h5d, {8{1'bx}}, 8'h3d, 8'h79, 8'ha2 ,SYNC_CTRL}; 44 | logic [BLOCK_W-1:0] marker_lane[LANE_N-1:0]; 45 | assign marker_lane[0] = MARKER_LANE0; 46 | assign marker_lane[1] = MARKER_LANE1; 47 | assign marker_lane[2] = MARKER_LANE2; 48 | assign marker_lane[3] = MARKER_LANE3; 49 | 50 | /* fsm */ 51 | reg sync_q; 52 | logic sync_next; 53 | reg first_q; // we have found the 1st alignement marker 54 | logic first_next; 55 | reg lock_q; 56 | logic lock_next; 57 | reg invalid_q; 58 | logic invalid_next; 59 | 60 | 61 | // counters 62 | reg [NV_CNT_W-1:0] nv_cnt_q; 63 | logic [NV_CNT_W-1:0] nv_cnt_next; 64 | logic [NV_CNT_W-1:0] nv_cnt_add;// am_invld_cnt 65 | logic nv_cnt_add_of; 66 | logic nv_cnt_rst_v; 67 | logic nv_cnt_4; 68 | logic nv_cnt_zero; 69 | 70 | // match alignement marker 71 | logic am_first_v; // found a valid alignement marker, lite version 72 | logic am_v; 73 | logic slip_v; 74 | 75 | logic gap_zero; 76 | logic lane_match_same; 77 | 78 | assign nv_cnt_rst_v = sync_q | ( lock_q & gap_zero & lane_match_same ); 79 | // add 80 | assign { nv_cnt_add_of, nv_cnt_add } = nv_cnt_q + {{NV_CNT_W-1{1'b0}}, lock_q & gap_zero & ~lane_match_same}; 81 | assign nv_cnt_4 = nv_cnt_add_of; 82 | assign nv_cnt_zero = ~|nv_cnt_q; 83 | 84 | assign nv_cnt_next = nv_cnt_rst_v ? {NV_CNT_W{1'b0}} : nv_cnt_add; 85 | 86 | // gap 87 | reg [GAP_W-1:0] gap_q; 88 | logic [GAP_W-1:0] gap_next; 89 | logic [GAP_W-1:0] gap_add; 90 | logic unused_gap_add_of; 91 | logic gap_rst_v; 92 | 93 | assign gap_rst_v = invalid_q | slip_v; 94 | assign {unused_gap_add_of, gap_add} = gap_q + { {GAP_W-1{1'b0}},1'b1 }; 95 | assign gap_next = gap_rst_v ? {GAP_W{1'b0}}: gap_add; 96 | 97 | assign gap_zero = ~|gap_q; 98 | 99 | always @(posedge clk) begin 100 | nv_cnt_q <= nv_cnt_next; 101 | gap_q <= gap_next; 102 | end 103 | 104 | // alignement marker detection 105 | // current lane 106 | reg [LANE_N-1:0] lane_q; 107 | logic [LANE_N-1:0] lane_next; 108 | logic [LANE_N-1:0] lane_match; 109 | 110 | // match same the alignement marker on the same lane 111 | assign lane_match_same = |(lane_match == lane_q) & nv_cnt_zero; 112 | genvar i; 113 | generate 114 | for( i=0 ; i < LANE_N; i++ ) begin : gen_lan_match_loop 115 | assign lane_match[i] = (marker_lane[i][3*8+2-1:0] == block_i[3*8+2-1:0] ) 116 | & (marker_lane[i][7*8+2-1:34] == block_i[7*8+2-1:34]); 117 | end 118 | endgenerate 119 | 120 | assign lane_next = gap_zero ? lane_match : lane_q; 121 | always @(posedge clk) begin 122 | lane_q <= lane_next; 123 | end 124 | 125 | assign am_first_v = |lane_match; 126 | assign am_v = gap_zero & lane_match_same; 127 | assign slip_v = sync_q & ~|lane_match 128 | | first_q & gap_zero & ~lane_match_same 129 | | lock_q & nv_cnt_4; 130 | 131 | 132 | // fsm 133 | assign invalid_next = ~valid_i; 134 | assign sync_next = valid_i 135 | & ( invalid_q 136 | | sync_q & ~am_first_v 137 | | slip_v); 138 | assign first_next = valid_i 139 | & ( sync_q & am_first_v 140 | | first_q & ~gap_zero); 141 | assign lock_next = valid_i 142 | & (first_q & am_v 143 | |lock_q & ~nv_cnt_4); 144 | 145 | always @(posedge clk) begin 146 | if ( ~nreset ) begin 147 | invalid_q <= 1'b1; 148 | sync_q <= 1'b0; 149 | lock_q <= 1'b0; 150 | first_q <= 1'b0; 151 | end else begin 152 | invalid_q <= invalid_next; 153 | sync_q <= sync_next; 154 | lock_q <= lock_next; 155 | first_q <= first_next; 156 | end 157 | end 158 | // output 159 | assign lock_v_o = lock_q; 160 | assign lane_o = lane_q; 161 | 162 | //assign slip_v_o = slip_v; 163 | 164 | assign lite_am_v_o = am_first_v; 165 | assign lite_lock_v_o = first_q | lock_q; 166 | 167 | `ifdef FORMAL 168 | logic [3:0] f_fsm; 169 | assign f_fsm = { invalid_q , sync_q , first_q, lock_q }; 170 | 171 | always @(posedge clk) begin 172 | if ( nreset ) begin 173 | // xcheck 174 | xcheck_valid_i : assert( ~$isunknown(valid_i)); 175 | xcheck_block_i : assert( ~ valid_i | valid_i & ~$isunknown(block_i)); 176 | xcheck_lock_v_o : assert( ~$isunknown(lock_v_o)); 177 | xcheck_lane_o : assert( ~lock_v_o | lock_v_o & ~$isunknown(lane_o)); 178 | // xcheck_slip_v_o : assert( ~$isunknown(slip_v_o)); 179 | 180 | // fsm 181 | sva_fsm_onehot : assert( $onehot(f_fsm)); 182 | end 183 | end 184 | `endif 185 | endmodule 186 | -------------------------------------------------------------------------------- /tb/am_lock_rx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `define SYNC_CTRL 2'b10 9 | `define SYNC_DATA 2'b01 10 | 11 | `define TB_TEST3_LOOP 150 12 | 13 | /* Testbench for RX frame sync */ 14 | module am_lock_rx_tb; 15 | 16 | localparam HEAD_W = 2; 17 | localparam DATA_W = 64; 18 | localparam BLOCK_W = HEAD_W + DATA_W; 19 | localparam [BLOCK_W-1:0] 20 | MARKER_LANE0 = { {8{1'bx}},8'hb8, 8'h89, 8'h6f, {8{1'bx}}, 8'h47, 8'h76, 8'h90 ,`SYNC_CTRL}, 21 | MARKER_LANE1 = { {8{1'bx}},8'h19, 8'h3b, 8'h0f, {8{1'bx}}, 8'he6, 8'hc4, 8'hf0 ,`SYNC_CTRL}, 22 | MARKER_LANE2 = { {8{1'bx}},8'h64, 8'h9a, 8'h3a, {8{1'bx}}, 8'h9b, 8'h65, 8'hc5 ,`SYNC_CTRL}, 23 | MARKER_LANE3 = { {8{1'bx}},8'hc2, 8'h86, 8'h5d, {8{1'bx}}, 8'h3d, 8'h79, 8'ha2 ,`SYNC_CTRL}; 24 | localparam LANE_N = 4; 25 | localparam LANE_W = $clog2(LANE_N); 26 | localparam GAP_N = 16383; 27 | 28 | reg clk = 1'b0; 29 | logic nreset; 30 | logic valid_i; // data valid 31 | logic [BLOCK_W-1:0] block_i; 32 | logic slip_v_o; // slip_done 33 | logic lock_v_o; // rx_block_lock 34 | 35 | /* verilator lint_off UNUSEDSIGNAL*/ 36 | logic lite_am_v_o; 37 | logic lite_lock_v_o; 38 | /* verilator lint_on UNUSEDSIGNAL*/ 39 | 40 | logic [LANE_N-1:0] lane_o; 41 | 42 | /* verilator lint_off BLKSEQ */ 43 | always clk = #5 ~clk; 44 | /* verilator lint_on BLKSEQ */ 45 | 46 | logic [BLOCK_W-1:0] marker_lane[LANE_N-1:0]; 47 | assign marker_lane[0] = MARKER_LANE0; 48 | assign marker_lane[1] = MARKER_LANE1; 49 | assign marker_lane[2] = MARKER_LANE2; 50 | assign marker_lane[3] = MARKER_LANE3; 51 | 52 | task send_rand_block(int cycles); 53 | logic [HEAD_W-1:0] head; 54 | logic [DATA_W-1:0] data; 55 | for(int t=0; t 1 ) begin 102 | logic unused_inc_of; 103 | { unused_inc_of, inc } = l + {{LANE_W-1{1'b0}}, 1'b1}; 104 | end else begin 105 | inc = ~l; 106 | end 107 | return inc; 108 | endfunction 109 | 110 | logic [LANE_W-1:0] tb_lane; 111 | 112 | initial begin 113 | $dumpfile("wave/am_lock_rx_tb.vcd"); 114 | $dumpvars(0, am_lock_rx_tb); 115 | // AM_LOCK_INIT -> AM_RESET_CNT 116 | nreset = 1'b0; 117 | #10; 118 | nreset = 1'b1; 119 | valid_i = 1'b1; 120 | block_i = {BLOCK_W{1'b0}}; 121 | $display("Starting test"); 122 | // test 1 123 | // AM_RESET_CNT -> FIND_1ST -> COUNT_1 -> COMP_2ND -> 2_GOOD -> 124 | // COUNT_2 125 | // Begining simple lock process right after reset 126 | // sending 2 correct aligngment marker and checking 127 | // we have locked on correct lane 128 | $display("test 1 %t", $time); 129 | tb_lane = LANE_W'($random % LANE_N); 130 | aquire_lock(10, tb_lane); 131 | 132 | // test 2 133 | // COUNT_2 -> SLIP 134 | // Send random data and send 4 invalid am to provoque slip 135 | $display("test 2 %t", $time); 136 | send_rand_block( GAP_N*4 ); 137 | assert(slip_v_o); 138 | assert(~lock_v_o); 139 | 140 | // test 3 141 | // RESET -> COMP_2ND -> SLIP 142 | // Send 2 valid am but each for a different lanes 143 | $display("test 3 %t", $time); 144 | #10 145 | tb_lane = LANE_W'($random % LANE_N); 146 | block_i = marker_lane[tb_lane]; 147 | send_rand_block(GAP_N); 148 | #10 149 | tb_lane = inc_lane(tb_lane); 150 | block_i = marker_lane[tb_lane]; 151 | #1 152 | assert(slip_v_o); 153 | #9 154 | assert(~lock_v_o); 155 | 156 | // test 4 157 | // Lose signal on SLIP/RST 158 | $display("test 4 %t", $time); 159 | #10 160 | valid_i = 1'b0; 161 | #1 162 | assert(~slip_v_o); 163 | #9 164 | assert(~lock_v_o); 165 | 166 | // test 5 167 | // Find 1 and then lose signal 168 | $display("test 5 %t", $time); 169 | #10 170 | valid_i = 1'b1; 171 | block_i = marker_lane[tb_lane]; 172 | #1 173 | assert(~slip_v_o); 174 | #9 175 | valid_i = 1'b0; 176 | #1 177 | assert(~slip_v_o); 178 | #9 179 | 180 | // test 6 181 | // Lock on lane then lose signal 182 | $display("test 6 %t", $time); 183 | valid_i = 1'b1; 184 | aquire_lock(1, tb_lane); 185 | assert(~slip_v_o); 186 | assert(lock_v_o); 187 | assert(lane_o[tb_lane]); 188 | #10 189 | valid_i = 1'b0; 190 | #1 191 | assert(~slip_v_o); 192 | #9 193 | assert(~lock_v_o); 194 | 195 | $display("Test finished"); 196 | $finish; 197 | end 198 | 199 | am_lock_rx #(.BLOCK_W(BLOCK_W), .LANE_N(LANE_N)) 200 | m_uut( 201 | .clk(clk), 202 | .nreset(nreset), 203 | .valid_i(valid_i), 204 | .block_i(block_i), 205 | .lock_v_o(lock_v_o), 206 | 207 | .lite_am_v_o(lite_am_v_o), 208 | .lite_lock_v_o(lite_lock_v_o), 209 | 210 | .lane_o(lane_o) 211 | ); 212 | 213 | /* Slip signal is no longer used in top but is quite 214 | * usefull for debug, faking output signal */ 215 | assign slip_v_o = m_uut.slip_v; 216 | 217 | endmodule 218 | -------------------------------------------------------------------------------- /tb/deskew_rx_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `define TB_LOOP_CNT 3 9 | `define TB_MARKER {BLOCK_W{1'b1}} 10 | `define SYNC_CTRL 2'b10 11 | `define SYNC_DATA 2'b01 12 | `define TB_SKEW_RANGE 5 13 | 14 | module deskew_rx_tb; 15 | localparam LANE_N = 4; 16 | localparam BLOCK_W = 66; 17 | localparam MAX_SKEW_BIT_N = 1856; 18 | localparam MAX_SKEW_BLOCK_N = ( MAX_SKEW_BIT_N - BLOCK_W -1 )/BLOCK_W; 19 | localparam [BLOCK_W-1:0] 20 | MARKER_LANE0 = { `SYNC_CTRL, 8'h0,8'hb8, 8'h89, 8'h6f, 8'h0, 8'h47, 8'h76, 8'h90 }, 21 | MARKER_LANE1 = { `SYNC_CTRL, 8'h0,8'h19, 8'h3b, 8'h0f, 8'h0, 8'he6, 8'hc4, 8'hf0 }, 22 | MARKER_LANE2 = { `SYNC_CTRL, 8'h0,8'h64, 8'h9a, 8'h3a, 8'h0, 8'h9b, 8'h65, 8'hc5 }, 23 | MARKER_LANE3 = { `SYNC_CTRL, 8'h0,8'hc2, 8'h86, 8'h5d, 8'h0, 8'h3d, 8'h79, 8'ha2 }; 24 | 25 | logic [BLOCK_W-1:0] marker_lane[LANE_N-1:0]; 26 | assign marker_lane[0] = MARKER_LANE0; 27 | assign marker_lane[1] = MARKER_LANE1; 28 | assign marker_lane[2] = MARKER_LANE2; 29 | assign marker_lane[3] = MARKER_LANE3; 30 | 31 | 32 | reg clk = 1'b0; 33 | logic nreset; 34 | 35 | logic [LANE_N-1:0] am_lite_v_i; 36 | logic [LANE_N-1:0] am_lite_lock_v_i; 37 | logic [LANE_N*BLOCK_W-1:0] data_i; 38 | logic [LANE_N*BLOCK_W-1:0] data_o; 39 | /* verilator lint_off UNUSEDSIGNAL*/ 40 | logic am_v_o; 41 | /* verilator lint_on UNUSEDSIGNAL*/ 42 | 43 | logic [BLOCK_W-1:0] tb_data_i[LANE_N-1:0]; 44 | logic [BLOCK_W-1:0] tb_data_o[LANE_N-1:0]; 45 | 46 | /*verilator lint_off BLKSEQ */ 47 | always clk = #5 ~clk; 48 | /*verilator lint_on BLKSEQ */ 49 | 50 | int skew[LANE_N]; 51 | int skew_max; 52 | task create_skew(); 53 | assert(`TB_SKEW_RANGE <= MAX_SKEW_BLOCK_N ); 54 | skew_max = 0; 55 | for(int i=0; i < LANE_N; i++ ) begin 56 | skew[i] = $random % `TB_SKEW_RANGE; 57 | //skew[i] = $random % 2; 58 | skew[i] = ( skew[i] < 0 )? -skew[i] : skew[i]; 59 | skew_max = ( skew_max > skew[i])? skew_max : skew[i]; 60 | end 61 | endtask 62 | 63 | task test_deskew(); 64 | assert( skew_max <= MAX_SKEW_BLOCK_N ); 65 | 66 | for(int j=0; j <= skew_max; j++ ) begin 67 | #10; 68 | `ifdef DEBUG 69 | $display("t %t skew %d", $time, j); 70 | `endif 71 | for(int i=0; i< LANE_N; i++) begin 72 | `ifdef DEBUG 73 | $display("lane %d j %d skew %d", i, j, skew[i]); 74 | `endif 75 | tb_data_i[i] = BLOCK_W'({ $random, $random, $random }); 76 | am_lite_lock_v_i[i] = 1'b0; 77 | am_lite_v_i[i] = 1'b0; 78 | if ( j == skew[i] ) begin 79 | tb_data_i[i] = marker_lane[i]; 80 | am_lite_v_i[i] = 1'b1; 81 | end 82 | if ( j > skew[i] ) begin 83 | `ifdef DEBUG 84 | $display("Set lock for lane %d", i); 85 | `endif 86 | am_lite_lock_v_i[i] = 1'b1; 87 | end 88 | end 89 | end 90 | /* complete lock */ 91 | #10 92 | am_lite_v_i = '0; 93 | am_lite_lock_v_i = '1; 94 | #1 95 | /* first cycle where lanes are aligned, we expect 96 | * to see the alignement marker on all lanes. 97 | * Checking the match between a lane and the correct 98 | * alignement marker */ 99 | for(int i=0; i 1 ? CNT_N-1:0, 19 | parameter KEEP_NEXT_MSB = CNT_N > 1 ? (CNT_N-1)*KEEP_W-1 : 0, 20 | parameter LANE0_CNT_N = IS_10G ? 2 : 1, 21 | parameter FULL_KEEP_W = CNT_N*KEEP_W, 22 | parameter BLOCK_TYPE_W = 8, 23 | parameter CTRL_W = 7 24 | )( 25 | // data clk 26 | //input clk, 27 | //input nreset, 28 | 29 | input ctrl_v_i, 30 | input idle_v_i, 31 | input [LANE0_CNT_N-1:0] start_v_i, 32 | input term_v_i, 33 | 34 | /* verilator lint_off UNUSEDSIGNAL*/ 35 | // TODO : add encoding of error 36 | input err_v_i, 37 | /* verilator lint_on UNUSEDSIGNAL*/ 38 | 39 | input [DATA_W-1:0] data_i, // tx data 40 | input [KEEP_W-1:0] keep_i, 41 | 42 | /* verilator lint_off ASCRANGE */ 43 | /* verilator lint_off UNUSEDSIGNAL*/ 44 | input [PART_MSB:0] part_i, 45 | input [KEEP_NEXT_MSB:0] keep_next_i, 46 | /* verilator lint_on UNUSEDSIGNAL*/ 47 | /* verilator lint_on ASCRANGE */ 48 | 49 | output head_v_o, 50 | output [1:0] sync_head_o, 51 | output [DATA_W-1:0] data_o 52 | ); 53 | /* verilator lint_off UNUSEDPARAM*/ 54 | localparam [BLOCK_TYPE_W-1:0] 55 | BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT 56 | BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT 57 | BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT 58 | BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT 59 | BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT 60 | BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT 61 | BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT 62 | BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT 63 | BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT 64 | BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT 65 | BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT 66 | BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT 67 | BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT 68 | BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT 69 | BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT 70 | /* verilator lint_on UNUSEDPARAM*/ 71 | 72 | localparam [CTRL_W-1:0] CTRL_IDLE = 7'h00; 73 | 74 | logic part_zero; 75 | // block type 76 | logic [FULL_KEEP_W-1:0] block_keep; 77 | logic [FULL_KEEP_W-1:0] term_mask_lite; 78 | logic unused_term_mask_lite_of; 79 | logic [BLOCK_TYPE_W-1:0] term_block_type; 80 | // block type field 81 | logic block_type_v; 82 | logic [BLOCK_TYPE_W-1:0] block_type; 83 | assign block_type_v = ctrl_v_i; 84 | 85 | generate 86 | 87 | if ( IS_10G ) begin : gen_is_10g_block_type 88 | 89 | assign block_type = {BLOCK_TYPE_W{start_v_i[0] }} & BLOCK_TYPE_START_0 90 | | {BLOCK_TYPE_W{start_v_i[1] }} & BLOCK_TYPE_START_4 91 | | {BLOCK_TYPE_W{term_v_i}} & term_block_type 92 | | {BLOCK_TYPE_W{idle_v_i}} & BLOCK_TYPE_CTRL; 93 | 94 | end else begin : gen_10g_block_type 95 | 96 | // start signal can only be sent on lower order byte 97 | assign block_type = {BLOCK_TYPE_W{start_v_i}} & BLOCK_TYPE_START_0 98 | | {BLOCK_TYPE_W{term_v_i}} & term_block_type 99 | | {BLOCK_TYPE_W{idle_v_i}} & BLOCK_TYPE_CTRL; 100 | end /* !IS_10G */ 101 | 102 | // terminate block type 103 | if ( CNT_N > 1 ) begin : gen_cnt_n_gt_1_block_keep 104 | assign block_keep = { keep_next_i, keep_i }; 105 | end else begin : gen_cnt_n_eq_1_block_keep 106 | assign block_keep = keep_i; 107 | end 108 | 109 | endgenerate 110 | 111 | assign { unused_term_mask_lite_of, term_mask_lite } = block_keep + {{FULL_KEEP_W-1{1'b0}}, 1'b1}; 112 | always @(term_mask_lite) begin 113 | case ( term_mask_lite ) 114 | 8'b00000001 : term_block_type = BLOCK_TYPE_TERM_0; 115 | 8'b00000010 : term_block_type = BLOCK_TYPE_TERM_1; 116 | 8'b00000100 : term_block_type = BLOCK_TYPE_TERM_2; 117 | 8'b00001000 : term_block_type = BLOCK_TYPE_TERM_3; 118 | 8'b00010000 : term_block_type = BLOCK_TYPE_TERM_4; 119 | 8'b00100000 : term_block_type = BLOCK_TYPE_TERM_5; 120 | 8'b01000000 : term_block_type = BLOCK_TYPE_TERM_6; 121 | 8'b10000000 : term_block_type = BLOCK_TYPE_TERM_7; 122 | default : term_block_type = 'X; 123 | endcase 124 | end 125 | // data 126 | logic [DATA_W-BLOCK_TYPE_W-1:0] data_ctrl; 127 | assign data_ctrl = idle_v_i ? {8{CTRL_IDLE}} : data_i[DATA_W-1:BLOCK_TYPE_W]; 128 | // output data 129 | assign data_o = { data_ctrl , block_type_v ? block_type : data_i[BLOCK_TYPE_W-1:0] }; 130 | // sync header data or control 131 | // data 2'b01 132 | // cntr 2'b10 133 | assign head_v_o = part_zero; 134 | assign sync_head_o = { ctrl_v_i, ~ctrl_v_i }; 135 | 136 | // FSM 137 | assign part_zero = part_i == 'd0; 138 | 139 | `ifdef FORMAL 140 | 141 | logic data_v_f; 142 | assert data_v_f = ~(idle_v_i | err_v_i ); 143 | initial begin 144 | // assume reset 145 | a_nreset : assume( ~nreset ); 146 | end 147 | 148 | always_comb begin 149 | // xcheck 150 | sva_xcheck_ctrl_i : assert( ~$isunknown({idle_v_i, start_v_i, term_v_i, err_v_i })); 151 | sva_xcheck_keep_i : assert ( ~data_v_f | data_v_f & ~$isunknown( keep_i )); 152 | genvar f; 153 | generate 154 | for( f=0; f < KEEP_W; f++) begin 155 | assert( ~(data_v_f&keep_i[f]) | data_v_f & keep_i[f] & ~$isunknown(data_i[f*8+7:f*8])); 156 | end 157 | endgenerate 158 | if ( BLOCK_W != DATA_W ) begin 159 | sva_xcheck_part_i : assert( ~data_v_f | data_v_f & ~$isunknown( part_i )); 160 | sva_xcheck_keep_next_i : assert( ~data_v_f | data_v_f & ~$isunknown( keep_next_i )); 161 | end 162 | // control signals 163 | sva_ctrl_onehot: assert( ~(part_zero & ctrl_v_i) | part_zer & ctrl_v_i & ~$onehot({idle_v_i, start_v_i, term_v_i, err_v_i })); 164 | end 165 | `endif 166 | endmodule 167 | -------------------------------------------------------------------------------- /tb/vpi/tb_fifo.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | #ifndef TB_ITCH_H 9 | #define TB_ITCH_H 10 | 11 | #include 12 | #include 13 | #include "pcs_defs.h" 14 | /********* 15 | * Utils * 16 | *********/ 17 | 18 | /* 19 | * Evaluate to the address located @offset (relative) bytes after pointer @ptr. 20 | */ 21 | #define psum(ptr, offset)\ 22 | ((void *) (((uint8_t *) (ptr)) + ((size_t) (offset)))) 23 | 24 | /* 25 | * Evaluate to a bytewise subtraction between a and b. 26 | */ 27 | #define psub(a, b)\ 28 | ((size_t) (((uint8_t *) (a)) - ((uint8_t *) (b)))) 29 | 30 | /* 31 | * Evaluate to the offset of member @member in structure @type. 32 | */ 33 | #define offof(type, member) ((size_t) (&(((type *) 0)->member))) 34 | 35 | /* 36 | * Evaluate to the difference between offsets of member0 and member1 in structure @type. 37 | */ 38 | #define offbtw(type, member0, member1) (offof(type, member0) - offof(type, member1)) 39 | 40 | /* 41 | * Evaluate to the ref of the object of type @type that contains a member 42 | * @member whose location is @ptr. 43 | */ 44 | #define cntof(ptr, type, member)\ 45 | ((type *) (psum(ptr, - offof(type, member)))) 46 | #define cntof_def(type, var, ptr, member) \ 47 | type *var = cntof(ptr, type, member); 48 | 49 | /* 50 | * If @ptr if non-null, evaluate to the ref of the object of type @type that contains a member 51 | * @member whose location is @ptr. 52 | * Otherwise, evaluate to 0. 53 | */ 54 | #define cntofs(ptr, type, member)\ 55 | ({type *p = cntof(ptr, type, member); ((p) ? p : (type *) 0);}) 56 | 57 | /* 58 | * Evaluate to 1 if @ptr points to a valid element of the array of @nb 59 | * elements of size @elsize starting at @start. 60 | */ 61 | #define iselof(ptr, start, nb, size) (\ 62 | ((void *) (start) <= (void *) (ptr)) && \ 63 | ((void *) (ptr) < psum((void *) start, nb * size)) && \ 64 | (!(psub(ptr, start) % size)) \ 65 | ) 66 | 67 | #define malloc_(x) (x *) malloc(sizeof(x)) 68 | 69 | /********* 70 | * Slist * 71 | *********/ 72 | 73 | typedef struct slist slist; 74 | typedef struct slisth slisth; 75 | 76 | /* 77 | * Slist node. 78 | */ 79 | struct slist { 80 | 81 | /* Next. */ 82 | slist *next; 83 | 84 | }; 85 | 86 | /* 87 | * Slist head. 88 | */ 89 | struct slisth { 90 | 91 | /* Write (can only write). */ 92 | slist *write; 93 | 94 | /* Read (can read and write). */ 95 | slist *read; 96 | 97 | }; 98 | 99 | /************* 100 | * Slist ops * 101 | *************/ 102 | 103 | /* 104 | * Link @a -> @b. 105 | */ 106 | static inline void slist_link( 107 | slist *a, 108 | slist *b 109 | ) {a->next = b;} 110 | 111 | /* 112 | * Unlink @a -> @b. 113 | */ 114 | static inline void slist_unlink( 115 | slist *a, 116 | slist *b 117 | ) { 118 | assert(a->next == b); 119 | a->next = 0; 120 | } 121 | 122 | /* 123 | * Insert @b element after @a. 124 | */ 125 | static inline void slist_insert( 126 | slist *a, 127 | slist *b 128 | 129 | ) 130 | { 131 | slist *c = a->next; 132 | a->next = b; 133 | b->next = c; 134 | } 135 | 136 | /* 137 | * Unlink @a and its successor, return the successor. 138 | */ 139 | static inline slist *slist_pop( 140 | slist *a 141 | ) { 142 | slist *b = a->next; 143 | a->next = 0; 144 | return b; 145 | } 146 | 147 | /************** 148 | * Slisth ops * 149 | **************/ 150 | 151 | /* 152 | * Initialize @head. 153 | */ 154 | static inline void slisth_init( 155 | slisth *head 156 | ) 157 | { 158 | head->read = 0; 159 | head->write = 0; 160 | } 161 | 162 | /* 163 | * Push @a in @head. 164 | */ 165 | static inline void slisth_push( 166 | slisth *head, 167 | slist *a 168 | ) 169 | { 170 | assert((!head->read) == (!head->write)); 171 | slist *write = head->write; 172 | if (write) write->next = a; 173 | a->next = 0; 174 | head->write = a; 175 | if (!head->read) head->read = a; 176 | } 177 | 178 | /* 179 | * If @head has at least one node, pull the node at read position 180 | * and return it. 181 | * Otherwise, return 0. 182 | */ 183 | static inline slist *slisth_pull( 184 | slisth *head 185 | ) 186 | { 187 | assert((!head->read) == (!head->write)); 188 | slist *a = head->read; 189 | if (!a) return 0; 190 | head->read = a->next; 191 | if (!head->read) head->write = 0; 192 | a->next = 0; 193 | return a; 194 | } 195 | 196 | 197 | /* 198 | * Unpull @a into @slisth. 199 | * It will become the next element to be pulled. 200 | */ 201 | static inline void slisth_unpull( 202 | slisth *head, 203 | slist *a 204 | ) 205 | { 206 | assert((!head->read) == (!head->write)); 207 | slist *read = head->read; 208 | a->next = read; 209 | head->read = a; 210 | if (!head->write) head->read = a; 211 | } 212 | 213 | /******* 214 | * Rem * 215 | *******/ 216 | 217 | /* 218 | * PMA data buffer fifo, simply linked list. 219 | */ 220 | typedef struct { 221 | 222 | /* Elements of the same fifo sorted by recency. */ 223 | slist elems; 224 | 225 | /* Debug id. */ 226 | uint64_t debug_id; 227 | 228 | /* expected output data. */ 229 | block_s *data; 230 | 231 | /* Control field. */ 232 | ctrl_lite_s *ctrl; 233 | 234 | 235 | } tv_data_fifo_elem_t; 236 | 237 | typedef struct{ 238 | slisth elems; 239 | } tv_data_fifo_t; 240 | 241 | /* 242 | * Construct and return an itch fifo. 243 | */ 244 | tv_data_fifo_t * tb_data_fifo_ctor( 245 | void 246 | ); 247 | 248 | /* 249 | * Delete @fifo. 250 | */ 251 | void tb_data_fifo_dtor( 252 | tv_data_fifo_t *fifo 253 | ); 254 | 255 | /* 256 | * Construct and push an element in @fifo. 257 | */ 258 | void tb_data_fifo_push( 259 | tv_data_fifo_t *fifo, 260 | block_s *nv, 261 | ctrl_lite_s *ctrl, 262 | uint64_t debug_id 263 | ); 264 | 265 | /* 266 | * If @fifo is not empty, pop an element and return it. 267 | * Otherwise, return 0. 268 | */ 269 | block_s *tb_data_fifo_pop( 270 | tv_data_fifo_t *fifo, 271 | uint64_t *debug_id, 272 | ctrl_lite_s **ctrl 273 | ); 274 | 275 | /* 276 | * Print a descriptor for all elements of @fifo. 277 | */ 278 | void tb_data_print_fifo( 279 | tv_data_fifo_t *fifo 280 | ); 281 | 282 | 283 | /* 284 | * Print the content of a fifo element @elem 285 | */ 286 | void tb_data_print_elem( 287 | tv_data_fifo_elem_t *elem 288 | ); 289 | 290 | 291 | 292 | 293 | #endif // TB_ITCH_H 294 | 295 | -------------------------------------------------------------------------------- /tb/pcs_tb.sv: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2023, Julia Desmazes. All rights reserved. 2 | * 3 | * This work is licensed under the Creative Commons Attribution-NonCommercial 4 | * 4.0 International License. 5 | * 6 | * This code is provided "as is" without any express or implied warranties. */ 7 | 8 | `ifndef TB_LOOP_CNT_N 9 | `define TB_LOOP_CNT_N 40000 10 | `endif 11 | 12 | module pcs_tb; 13 | 14 | `define _40GBASE 15 | 16 | `ifdef _40GBASE 17 | localparam IS_10G = 0; 18 | localparam LANE_N = 4; 19 | `else 20 | localparam IS_10G = 1; 21 | localparam LANE_N = 1; 22 | `endif 23 | 24 | localparam HEAD_W = 2; 25 | localparam DATA_W = 64; 26 | localparam BLOCK_W = DATA_W + HEAD_W; 27 | localparam KEEP_W = DATA_W/8; 28 | localparam LANE0_CNT_N = !IS_10G ? 1 : 2; 29 | 30 | localparam MAX_SKEW_BIT_N = 1856; 31 | 32 | localparam DEBUG_ID_W = 64; 33 | 34 | // TX 35 | // MAC 36 | logic [LANE_N-1:0] ctrl_v_i; 37 | logic [LANE_N-1:0] idle_v_i; 38 | logic [LANE_N-1:0] start_v_i; 39 | logic [LANE_N-1:0] term_v_i; 40 | logic [LANE_N-1:0] err_v_i; 41 | logic [LANE_N*DATA_W-1:0] data_i; // tx data 42 | logic [LANE_N*KEEP_W-1:0] keep_i; 43 | logic ready_o; 44 | // GEARBOX 45 | logic [LANE_N*BLOCK_W-1:0] gb_data_o; 46 | logic [LANE_N*BLOCK_W-1:0] tb_gb_data; 47 | logic [LANE_N*BLOCK_W-1:0] tb_gb_data_diff; 48 | 49 | // debug id 50 | logic [LANE_N*DEBUG_ID_W-1:0] data_debug_id; 51 | logic [LANE_N*DEBUG_ID_W-1:0] tb_data_debug_id; 52 | 53 | // RX 54 | // transiver 55 | logic [LANE_N-1:0] serdes_v_i; 56 | logic [LANE_N*DATA_W-1:0] serdes_data_i; 57 | logic [LANE_N*HEAD_W-1:0] serdes_head_i; 58 | logic [LANE_N-1:0] gearbox_slip_o; 59 | // MAC 60 | logic [LANE_N-1:0] valid_o; 61 | logic [LANE_N-1:0] ctrl_v_o; 62 | logic [LANE_N-1:0] idle_v_o; 63 | logic [LANE_N*LANE0_CNT_N-1:0] start_v_o; 64 | logic [LANE_N-1:0] term_v_o; 65 | logic [LANE_N-1:0] err_v_o; 66 | logic [LANE_N-1:0] ord_v_o; 67 | logic [LANE_N*DATA_W-1:0] data_o; 68 | logic [LANE_N*KEEP_W-1:0] keep_o; 69 | 70 | 71 | reg clk = 1'b0; 72 | logic nreset; 73 | logic tb_nreset; 74 | 75 | /*verilator lint_off BLKSEQ */ 76 | always clk = #5 ~clk; 77 | /*verilator lint_on BLKSEQ */ 78 | 79 | assign tb_gb_data_diff = tb_gb_data ^ gb_data_o; 80 | 81 | 82 | /* Check TX data against tb produced expected output */ 83 | always @(posedge clk) begin 84 | if( nreset ) begin 85 | assert(tb_gb_data == gb_data_o); 86 | `ifdef VERILATOR 87 | if( tb_gb_data != gb_data_o)begin 88 | $display("ERROR : time %t pma data not matching, :\ngb_data_o %x\ntb_gb_data_o %x\ndebug id %x\ndiff %x\n",$time, gb_data_o, tb_gb_data, tb_data_debug_id, tb_gb_data_diff); 89 | end 90 | `ifdef DEBUG 91 | else begin 92 | $display("PASS : time %t pma output matching tb\n",$time); 93 | end 94 | `endif 95 | `endif 96 | end 97 | end 98 | 99 | 100 | /* Check RX output against TX input 101 | * This is only checking once lock has been 102 | * exstablished, this takes at least 2 alignement 103 | * markers in 40GBASE. 104 | * Doesn't account for skew delay : only works when 105 | * there is no skew */ 106 | genvar x; 107 | generate 108 | for(x=0; x