diff --git a/.gitignore b/.gitignore index aa98769..e2f8749 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ output/ *.log *.jou *.txt +*.mem __pycache__ logs/ -gen/ \ No newline at end of file +gen/ +verilog/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c9c23d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/blue-wrapper"] + path = lib/blue-wrapper + url = https://github.com/wengwz/blue-wrapper.git diff --git a/README.md b/README.md index 2fae0e9..1479fee 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,35 @@ # Blue-CRC -# Overview +## Overview -The [Cyclic Redundancy Code(CRC)](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) is an error-detecting code commonly used in digital network and storage devices to detect accidental changes to digital data. The principle of CRC is that a small digest of original data, which is similar to what is generated by hash function, is calculated and appended to original data before transmission and in the receiving end CRC is calculated on the received data again and compared with appended digest to check consistency. The CRC calculation is widely used in many fields especially in the network transmission, the standard protocols likes TCP/IP or RDMA all require CRC calculations. With the development of network technology, the speed of transmission is improved greatly which also challenges the high-performance implementation of CRC. This repo implements a highly parameterized, parallel, pipelined and high-throughput hardware of CRC calculation using [Bluespec System Verilog](https://github.com/B-Lang-org/bsc). +Cyclic Redundancy Check(CRC) is an error-detection mechanism widely used in communication systems. The CRC algorithm behaves like a hash function, specifically, it calculates an unique identifier(checksum) for original data, and then uses this identifier to determine whether the data has changed after transmission. For a specific system, CRC can be implemented either through software codes or hardware circuit. And this repo provides a highly parameterized, parallel, pipelined and high-throughput hardware implementation of CRC algorithm using [Bluespec SystemVerilog](https://github.com/B-Lang-org/bsc). -## Features +### Main Features -The main features of the implemented CRC IP are listed as follows: +The main features of the implemented CRC circuit are listed as follows: -- Complete CRC Configuration: The IP supports complete CRC configuration parameters including polynomial, initVal(the initial CRC value), finalXor(the result is xor’d with this value if desired), reflectData(if True, reverse the input bit order), reflectRemainder(if True, reverse the result bit order). -- Standard Interface: The input interface supports AxiStream protocol whose data width is parameterized. And the outputCRC result is guarded by the basic handshake protocol. -- Parallel: The IP is designed to process multi-byte input at the same cycle. -- Fully Pipelined: The IP takes in one AxiStream frame every cycle. -- High Throughput: The IP configured with 256-bit input and 32-bit CRC output can runs at 500MHz on Xilinx xcvu9p FPGA. +- Complete CRC Configuration: The circuit supports complete CRC configuration parameters including polynomial, initVal(the initial CRC value), finalXor(the result is xor’d with this value if desired), reverseOutput(if True, reverse the input bit order), reverseInput(if True, reverse the result bit order). +- Standard I/O Interface: The input interface supports AXI-Stream protocol whose data width is parameterized. And CRC result is transmitted following the basic valid-ready handshake protocol. +- Parallel and Pipelined: The circuit is designed to process multiple bytes per cycle, and can reach the working frequency of 500MHz on Xilinx xcvu9p FPGA. +- High Throughput: The circuit configured with 256-bit input and 32-bit CRC output reaches at most throughput of 128Gb/s. +- Two computing modes: The circuit supports both CRC generation mode for the sender and CRC verification mode for the receiver. -# Theory of Operation ## Algorithm Theory The idea of our parallel and high-performance CRC implementation comes from the [paper](https://ieeexplore.ieee.org/abstract/document/5501903) and its corresponding [open-source implementation](https://bitbucket.org/spandeygit/crc32_verilog). And main contributions of this repo compared to the existing work include: - Parameterize the original design and provide a standard interface; -- Refine the logic implementation to improve the timing issue; +- Refine the logic implementation to improve the working frequency. +- Support both generation and verification modes for CRC calculation. -The calculation of CRC of original data is basically a division operation on two polynomials based on modulo-2 arithmetic and the remainder of this division is just the checksum we wanted. Consider a m-bit original data $b_{m-1}b_{m-2}b_{m-3}...b_{1}b_{0}$ can be represented as the polynomial M(x): +The calculation of CRC is basically a division operation on two polynomials based on modulo-2 arithmetic and the remainder of this division is just the checksum we wanted. Consider a m-bit original data $b_{m-1}b_{m-2}b_{m-3}...b_{1}b_{0}$ can be represented as the polynomial $M(x)$: $$ M(x)=b_{m-1}x^{m-1}+b_{m-2}x^{m-2}+...+b_{1}x+b_{0} $$ -And a predetermined (n+1)-bit generator polynomial can be represented as G(x): +And a predetermined (n+1)-bit generator polynomial can be represented as $G(x)$: $$ G(x)=b_{n}x^{n}+b_{n-1}x^{n-1}+...+b_{1}x^{1}+b_{0} @@ -38,14 +38,14 @@ $$ And the CRC is derived following the equation below: $$ -CRC=remainder(\frac{M(x)x^{n}}{G(x)}) +CRC[M(x)]=remainder(\frac{M(x)x^{n}}{G(x)}) $$ -And more detailed introduction of CRC can be accessed through this [link](https://en.wikipedia.org/wiki/Cyclic_redundancy_check). Based on the characteristics of modulo-2 arithmetic, CRC calculation can be easily implemented using a LFSR register as the figure shown below: +More detailed introduction of CRC can be accessed via this [link](https://en.wikipedia.org/wiki/Cyclic_redundancy_check). Based on the characteristics of modulo-2 arithmetic, CRC calculation can be easily implemented using a LFSR register as the figure shown below:
-However, the LFSR implementation takes only one-bit per cycle, which only provides a poor throughput. In order to get a parallel and high-performance CRC design, the serial implementation demonstrated above should be rearranged into a parallel configuration. And the following two theorems is used to achieve parallelism in CRC computation. +This circuit is one of the most classic hardware implementations of CRC algorithm, which only needs few hardware resources and can reach high working frequency. However, the LFSR implementation takes in only 1-bit per cycle, which provides a poor throughput. In order to get a parallel and high-performance CRC design, the serial implementation demonstrated above should be rearranged into a parallel architecture. And in blue-crc, the following two theorems is used to achieve parallelism in CRC computation. - Theorem 1: @@ -56,13 +56,13 @@ $$ - Theorem 2: $$ -CRC[x^kA(x)] = CRC[x^kCRC[A(x)]] +CRC[A(x)x^k] = CRC[CRC[A(x)]x^k] $$ -Theorem 1 indicates that original data of any length can be split into multiple pieces and CRC calculation of each piece can be performed parallel and then combined to get complete CRC result of original data. In our designed, the length of a piece is 8-bit and it’s assumed that the length of original data is multiples of bytes. For example, a N-bit original data, represented as polynomial $A(x)$, can be divided into n bytes, i.e. $N=8\times n$, each byte of original data is represented as the polynomial $A_{i}(x)$, and $A(x)$ can be expressed as: +Theorem 1 indicates that original data of any length can be split into multiple pieces and CRC checksum of each piece can be calculated in parallel and then add up to get the CRC result of complete data. In our designed, the length of one piece is 8-bit and it’s assumed that the length of original data is multiples of bytes. For example, a N-bit original data, represented as polynomial $A(x)$, can be divided into n bytes, i.e. $N=8\times n$, each byte of original data is represented as the polynomial $A_{i}(x)$, and $A(x)$ can be expressed as: $$ -A(x)=\sum_{i=0}^{n}A_i(x)\ x^{8i} +A(x)=\sum_{i=0}^{n-1}A_i(x)\ x^{8i} $$ And the CRC of A(x) is derived as below: @@ -70,192 +70,50 @@ And the CRC of A(x) is derived as below: $$ -CRC(A(x))=CRC(A_{n-1}(x)x^{8(n-1)}+CRC(A_{n-2}x^{8(n-2)}))+... +CRC(A_{1}(x)x^8)+ CRC(A_{0}(x)) +CRC(A(x))=CRC[A_{n-1}(x)x^{8(n-1)}]+CRC[A_{n-2}(x)x^{8(n-2)}]+... +CRC[A_{1}(x)x^8]+ CRC[A_{0}(x)] $$ -To calculate the CRC of each 8-bit piece, we can first compute the CRC results of all possible 8-bit input data and store them in a lookup table. When we need to compute CRC of a specific input, we can just search the precomputed table using the input data as index. +To calculate the CRC of each 8-bit piece, we don't have to implement real circuit instead it' more efficient to precompute the CRC results of all possible values of 8-bit data and store them in hardware lookup table. When we need to compute CRC, we can just search the precomputed table using the input data as index. -However, the parallel scheme proposed above is still impractical for hardware implementation. The length of original data is random and the hardware only supports fixed data width. What’s more, the length of input data is usually very large, especially for network application, and the required hardware resource grows with data length linearly. In hardware implementation, the width of input port of CRC component is usually fixed and original data is split into pieces of input port width and sent into the component serially. - -For example, the width of input port is fixed at 256 and original data is divided into pieces of 256-bit and sent into component in big-endian, i.e., high-bits are transmitted first. So the hardware needs to compute the CRC result in a iterative manner. In each iteration, component calculates the 256-bit input each cycle based on Theorem 1, and then combined with the CRC results of former bits, and finally the combined result is stored in registers for the next iteration. The combine of CRC of current input with that of former bits is based on Theorem 2. Take the first two iterations as example, the first iteration computes the CRC of first 256-bits of original data $CRC(A_{n-1}(x))$ and in the second iteration, the CRC of second 256-bits of original data $CRC(A_{n-2}(x))$ is computed first and then this two CRC is combined to get CRC of first 512-bits based on the equation below: +However, the parallel scheme proposed above is still impractical for hardware implementation. For real circuit design, the width of input data bus is usually fixed and original data is split into multiple frames and sent into the component serially. So the hardware needs to compute the CRC result in an accumulative manner. In each cycle, the circuit calculates CRC checksum of input frame based on Theorem 1, and then adds it to the intermediate CRC result of former frames. +The addition of CRC result of current input frame and the intermediate CRC result is based on Theorem 2. Assume that original data is transmitted in big-endian byte order, the width of the input bus is 256-bit, $A(x)$ represents data received in this cycle and $A'(x)$ represents data received in former cycles. And we need to add $CRC[A(x)]$ to the intermediate result $CRC[A'(x)]$ to get $ CRC[A'(x)x^{256} + A(x)]$. Based on Theorem 2, we can derive the equation below: $$ -CRC[A_{n-1}(x)x^{256}+CRC(A_{n-2}(x))]=CRC[x^{256}CRC(A_{n-1}(x))]+CRC[A_{n-2}(x)] +CRC[A'(x)x^{256}+A(x)]=CRC[A'(x)x^{256}]+CRC[A(x)]=CRC[CRC[A'(x)]\times x^{256}]+CRC[A(x)] +$$ +The equation shows that we need to shift the intermediate CRC result left, perform CRC calculation on it again and then add it with the CRC result of current frame. And the CRC calculation of the intermediate checksum can also be implemented using hardware lookup tables. For accumulation, there's one more case to consider, that is, the width of raw data may not just be multiples of 256-bit, so you cannot directly use the above formula for accumulation when processing the last input frame. Instead we need to dynamically calculate the width of valid data in the last frame of original data. Assume that the valid width of the last frame data is m, the accumulation is done following the equation below: +$$ +CRC[A'(x)x^m+A(x)]=CRC[CRC[A'(x)]\times x^m]+CRC[A(x)] $$ -Following the equation above, in the second iteration, the $CRC(A_{n-1}(x))$ got in the former iteration is multiplied by $x^{256}$ and then perform CRC calculation again before combined with the CRC of this iteration. This calculation can also be done using lookup table based on Theorem1. - -## Block Diagram - -Based on the parallel scheme proposed above, a simplified pipelined hardware architecture used in our design is shown in the figure below: - -
- -The main problem of this pipelined architecture is that it assumes that the length of original data just be multiples of input port width. But in real applications, this assumption is impractical especially for large port width like 256-bits and 512-bits. So additional data and control path is needed to handle this unalignment case. And our design has added this handling logic and supports calculating the CRC of original data with any length. The theory and corresponding structure to resolve unalignment case will be added to this document in the near future. - -## Parameters - -The following table lists the instantiation parameters of our CRC IP. - -| Parameter Name | Type | Description | Requirement | -| --- | --- | --- | --- | -| crcWidth | numeric type | The bit width of crc result | The crcWidth needs to be a multiple of 8 | -| polynomial | Bit#(crcWidth) | The value of generator polynomial | The value should be in the range of crcWidth-bit data | -| initVal | Bit#(crcWidth) | The initial value of crc | The value should be in the range of crcWidth-bit data | -| finalXor | Bit#(crcWidth) | The final result is xor’d with this value | The value should be in the range of crcWidth-bit data | -| reflectData | Bool | if True, reverse the bit order of each byte of input data | / | -| relectRemainder | Bool | if True, reverse the bit order of whole final result | / | -| dataWidth | numeric type | The bit width of input data | The dataWidth needs to be a multiple of 8 | -| dataByteNum | numeric type | The width of tkeep field in AxiStream | The dataByteNum equals to dataWidth/8 | - -## Hardware Interface - - The hardware design of this repo is implemented in Bluespec System Verilog. BSV ***module*** interacts with each other through ***interface*** which consists of subinterfaces or ***method***. The ***interface*** designed for our IP is named **CrcAxiStream** and consists of two subinterface: - -| Name | Type | Descriptions | -| --- | --- | --- | -| axiStreamIn | Put#( AxiStream#(dataByteNum, dataWidth) ) | The Put interface includes a method which takes in a struct of AxiStream type and returns Action. | -| crcResultOut | Get#(Bit#(crcWidth)) | The Get interface includes a method which returns ActionValue#(Bit#(crcWidth)). | - -The fields of AxiStream struct in BSV are listed below: - -| Name | Type | -| --- | --- | -| tData | Bit#(dataWidth) | -| tKeep | Bit#(tKeep) | -| tLast | Bool | -| tUser | Bool | - -Besides, the BSV implementation can also generate Verilog codes and all interface signals are listed as below: - -| Name | Direction | Type | -| --- | --- | --- | -| CLK | In | wire | -| RST_N | In | wire | -| s_axi_stream_tvalid | In | wire | -| s_axi_stream_tready | Out | wire | -| s_axi_stream_tlast | In | wire | -| s_axi_stream_tuser | In | wire | -| s_axi_stream_tdata | In | wire [DATA_WIDTH - 1 : 0] | -| s_axi_stream_tkeep | In | wire [KEEP_WIDTH - 1 : 0] | -| m_crc_stream_valid | Out | wire | -| m_crc_stream_ready | In | wire | -| m_crc_stream_data | Out | wire [CRC_WIDTH - 1 : 0] | - -## Input Format - -The CRC IP supports calculating CRC of original data consisting of arbitrary number of bytes. The original data is divided into small pieces of same size first and then the pieces are transmitted to the CRC component serially. Note that the data should be transmitted in the **big-endian** order, which means that data pieces of more significant bits are transmitted first and in a AxiStream transaction the most significant byte is placed in the lowest 8-bit of tData field. The figure below shows the example of transmitting 10-bytes original data and the width of tData is configured at 32-bit. Note that if the length of data is not a multiple of the width of tData, the bits in tKeep corresponds to null bytes should be clear. - -
- -# Area Usage and Frequency - -The area usage and frequency of our CRC IP are highly related to the settings of parameters, especially signal width parameters including ***dataWidth*** and ***crcWidth.*** In general, the area usage increases and the timing issue worsens as the value of these two parameters increase. - -In this repo, CRC modules under 12 different configurations are instantiated and then synthesized and implemented based on Xilinx **xcvu9p** device using **Vivado**. The area usage and timing are shown in tables below. -- Timing Report: The CRC instantiations are synthesized and implemented using Vivado under the timing constraint of 500Hz and the setup time slacks after routing are listed below: +## Hardware Architecture +The diagram below shows the hardware implementation of the parallel CRC algorithm introduced above. To improve the working frequency and throughput, the circuit design adopts an eight-stage pipelined architecture. The first five stages calculate the checksum of the input data and add it to the intermediate CRC result. The last three stages is used to handle the accumulation of the last frame of one packet, in which the width of valid data may be less than the width of bus. +
-| dataWidth | crcWidth | Setup Slack | -| --- | --- | --- | -| 64 | 8 | 0.190 ns | -| 128 | 8 | 0.218 ns | -| 256 | 8 | 0.098 ns | -| 512 | 8 | 0.093 ns | -| 64 | 16 | 0.320 ns | -| 128 | 16 | 0.132 ns | -| 256 | 16 | 0.140 ns | -| 512 | 16 | 0.078 ns | -| 64 | 32 | 0.112 ns | -| 128 | 32 | 0.124 ns | -| 256 | 32 | 0.001 ns | -| 512 | 32 | -0.410 ns | -- Area Usage: - -The detailed area usage under 3 different configurations are shown as follows: +## Area Usage and Frequency +The actual performance and hardware resources utilization of CRC circuits depend on the specific configuration parameters. In most cases, the throughput of the hardware circuit increases with the width of input data bus, and the hardware resources utilization is related to both data bus width and checksum width. Taking the 32-bit CRC checksum specified by the IEEE 802-3 protocol as an example, CRC circuit with input bus configured at 256-bit can reach the working frequency of 500MHz on Xilinx xcvu9p FPGA device, and the maximum throughput can reach 128Gb/s. The detailed utilization of different hardware resources is as follows: ```verilog ------------- -8-bit crcWidth and 256-bit dataWidth: ------------- -CLB Logic -+----------------------------+------+-------+------------+-----------+-------+ -| Site Type | Used | Fixed | Prohibited | Available | Util% | -+----------------------------+------+-------+------------+-----------+-------+ -| CLB LUTs | 6651 | 0 | 0 | 1182240 | 0.56 | -| LUT as Logic | 4067 | 0 | 0 | 1182240 | 0.34 | -| LUT as Memory | 2584 | 0 | 0 | 591840 | 0.44 | -| LUT as Distributed RAM | 2584 | 0 | | | | -| LUT as Shift Register | 0 | 0 | | | | -| CLB Registers | 3433 | 0 | 0 | 2364480 | 0.15 | -| Register as Flip Flop | 3433 | 0 | 0 | 2364480 | 0.15 | -| Register as Latch | 0 | 0 | 0 | 2364480 | 0.00 | -| CARRY8 | 0 | 0 | 0 | 147780 | 0.00 | -| F7 Muxes | 0 | 0 | 0 | 591120 | 0.00 | -| F8 Muxes | 0 | 0 | 0 | 295560 | 0.00 | -| F9 Muxes | 0 | 0 | 0 | 147780 | 0.00 | -+----------------------------+------+-------+------------+-----------+-------+ -BLOCKRAM -+----------------+------+-------+------------+-----------+-------+ -| Site Type | Used | Fixed | Prohibited | Available | Util% | -+----------------+------+-------+------------+-----------+-------+ -| Block RAM Tile | 0 | 0 | 0 | 2160 | 0.00 | -| RAMB36/FIFO* | 0 | 0 | 0 | 2160 | 0.00 | -| RAMB18 | 0 | 0 | 0 | 4320 | 0.00 | -| URAM | 0 | 0 | 0 | 960 | 0.00 | -+----------------+------+-------+------------+-----------+-------+ - ------------- -16-bit crcWidth and 256-bit dataWidth: ------------- -**CLB Logic -+----------------------------+------+-------+------------+-----------+-------+ -| Site Type | Used | Fixed | Prohibited | Available | Util% | -+----------------------------+------+-------+------------+-----------+-------+ -| CLB LUTs | 9556 | 0 | 0 | 1182240 | 0.81 | -| LUT as Logic | 5024 | 0 | 0 | 1182240 | 0.42 | -| LUT as Memory | 4532 | 0 | 0 | 591840 | 0.77 | -| LUT as Distributed RAM | 4532 | 0 | | | | -| LUT as Shift Register | 0 | 0 | | | | -| CLB Registers | 3720 | 0 | 0 | 2364480 | 0.16 | -| Register as Flip Flop | 3720 | 0 | 0 | 2364480 | 0.16 | -| Register as Latch | 0 | 0 | 0 | 2364480 | 0.00 | -| CARRY8 | 0 | 0 | 0 | 147780 | 0.00 | -| F7 Muxes | 0 | 0 | 0 | 591120 | 0.00 | -| F8 Muxes | 0 | 0 | 0 | 295560 | 0.00 | -| F9 Muxes | 0 | 0 | 0 | 147780 | 0.00 | -+----------------------------+------+-------+------------+-----------+-------+ -BLOCKRAM -+----------------+------+-------+------------+-----------+-------+ -| Site Type | Used | Fixed | Prohibited | Available | Util% | -+----------------+------+-------+------------+-----------+-------+ -| Block RAM Tile | 0 | 0 | 0 | 2160 | 0.00 | -| RAMB36/FIFO* | 0 | 0 | 0 | 2160 | 0.00 | -| RAMB18 | 0 | 0 | 0 | 4320 | 0.00 | -| URAM | 0 | 0 | 0 | 960 | 0.00 | -+----------------+------+-------+------------+-----------+-------+** - ------------- -32-bit crcWidth and 256-bit dataWidth: ------------- -CLB Logic +CLB Logic: +----------------------------+-------+-------+------------+-----------+-------+ | Site Type | Used | Fixed | Prohibited | Available | Util% | +----------------------------+-------+-------+------------+-----------+-------+ -| CLB LUTs | 20508 | 0 | 0 | 1182240 | 1.73 | -| LUT as Logic | 9708 | 0 | 0 | 1182240 | 0.82 | -| LUT as Memory | 10800 | 0 | 0 | 591840 | 1.82 | -| LUT as Distributed RAM | 10800 | 0 | | | | +| CLB LUTs | 21584 | 0 | 0 | 1182240 | 1.83 | +| LUT as Logic | 10636 | 0 | 0 | 1182240 | 0.90 | +| LUT as Memory | 10948 | 0 | 0 | 591840 | 1.85 | +| LUT as Distributed RAM | 10948 | 0 | | | | | LUT as Shift Register | 0 | 0 | | | | -| CLB Registers | 8494 | 0 | 0 | 2364480 | 0.36 | -| Register as Flip Flop | 8494 | 0 | 0 | 2364480 | 0.36 | +| CLB Registers | 9647 | 0 | 0 | 2364480 | 0.41 | +| Register as Flip Flop | 9647 | 0 | 0 | 2364480 | 0.41 | | Register as Latch | 0 | 0 | 0 | 2364480 | 0.00 | | CARRY8 | 0 | 0 | 0 | 147780 | 0.00 | | F7 Muxes | 0 | 0 | 0 | 591120 | 0.00 | | F8 Muxes | 0 | 0 | 0 | 295560 | 0.00 | | F9 Muxes | 0 | 0 | 0 | 147780 | 0.00 | +----------------------------+-------+-------+------------+-----------+-------+ -BLOCKRAM + +BLOCKRAM: +----------------+------+-------+------------+-----------+-------+ | Site Type | Used | Fixed | Prohibited | Available | Util% | +----------------+------+-------+------------+-----------+-------+ @@ -265,13 +123,136 @@ BLOCKRAM | URAM | 0 | 0 | 0 | 960 | 0.00 | +----------------+------+-------+------------+-----------+-------+ ``` -# User Interface -For BSV users, you can import our packages directly and instantiate CrcAxiStream interface in your codes. And for Verilog users, we also provide a script in [scripts/CrcGenerator.py](./scripts/CrcGenerator.py) to generate custom Verilog module automatically. In the header of this script, you can specify parameters of the custom CRC hardware you need and then run the script in the root directory by: + +## User Guide +### Configuration Parameters + +The following table lists complete configuration parameters of CRC hardware: + +| Name | Description | Requirement | +| --- | --- | --- | +| crc_width | The width of CRC checksum | The width needs to be multiples of 8-bit | +| axi_keep_width | The width of tkeep field in AXI-Stream protocol | / | +| polynomial | The value of generator polynomial | The value should be in the range restricted by crc_width | +| init_value | The initial value for CRC calculation | The value should be in the range restricted by crc_width | +| final_xor | The final result is xor’d with this value | The value should be in the range restricted by crc_width | +| reverse_input | if True, reverse the bit order of each input byte | Two available options: reverse or not_reverse | +| reverse_output | if True, reverse the bit order of final result | Two available options: reverse or not_reverse | +| mem_file_prefix| The name prefix of files containing lookup tables| /| +| crc_mode | The computation mode of CRC circuit | Two available modes: (1) SEND: appends zeros after raw data automatically and is used for CRC generation in the sender; (2) RECV: calculates the division of raw data by polynominal without appending zeros and is used for CRC verification in the receiver;| + + +### Input Format +CRC hardware receives input data from the upstream module based on the AXI-Stream bus protocol. And the output port of final result uses the valid-ready handshake mechanism to interact with the downstream module. The Verilog ports generated from the top-level module of BSV designs are as follows: +```verilog +module mkCrcRawAxiStreamCustom( + input CLK, + input RST_N, + + input s_axis_tvalid, + input s_axis_tdata, + input s_axis_tkeep, + input s_axis_tlast, + input s_axis_tuser, + output s_axis_tready, + + output m_crc_stream_data, + output m_crc_stream_valid, + input m_crc_stream_ready +); ``` -python3 scripts/CrcGenerator.py +When initiating CRC computation, the original data must be transmitted in big-endian byte order, that is, higher bytes need to be transmitted before lower bytes. Assuming that the width of the input AXI-Stream bus of CRC circuit is 32-bit(4-byte) and the width of raw data is 80-bit(10-byte), the transmission needs three cycles to complete and the AXI-Stream frames transmitted in each cycle is shown below: +
+ +### BSV Interface +The blue-crc is based on Bluespec SystemVerilog hardware description language, so for designers using BSV, CRC module can be used directly through instantiation. Detailed steps required to use it are as follows: +1. Get source codes: blue-crc uses the AXI-Stream interface provided by the blue-wrapper project, so you need to add --recursive option to get its codes when cloning: +```shell +git clone --recursive https://github.com/datenlord/blue-crc.git +``` +2. Import modules: +```Verilog +import CrcDefines :: *; +import CrcAxiStream :: *; +import AxiStreamTypes :: *; +``` +3. Specify Configuration Parameters: The **CrcConfig** struct encapsulates configuration parameters for CRC hardware circuits. The definition of **CrcConfig** struct is as follows: +```verilog +typedef struct { + Bit#(crcWidth) polynominal; + Bit#(crcWidth) initVal; + Bit#(crcWidth) finalXor; + IsReverseBitOrder revInput; + IsReverseBitOrder revOutput; + String memFilePrefix; + CrcMode crcMode; +} CrcConfig#(numeric type crcWidth) deriving(Eq, FShow); + +typedef enum { + CRC_MODE_RECV, + CRC_MODE_SEND +} CrcMode deriving(Eq, FShow); + +typedef enum { + BIT_ORDER_REVERSE, + BIT_ORDER_NOT_REVERSE +} IsReverseBitOrder deriving(Eq, FShow); +``` + +4. Instantiate **CrcAxiStream**: The top-level interface **CrcAxiStream** is defined as follows: +```verilog +typedef Bit#(width) CrcResult#(numeric type width); +typedef Get#(CrcResult#(crcWidth)) CrcResultGet#(numeric type crcWidth); +typedef Put#(AxiStream#(keepWidth, AXIS_USER_WIDTH)) AxiStreamPut#(numeric type keepWidth); + +interface CrcAxiStream#(numeric type crcWidth, numeric type axiKeepWidth); + interface AxiStreamPut#(axiKeepWidth) crcReq; + interface CrcResultGet#(crcWidth) crcResp; +endinterface +``` + +Take the 32-bit CRC specified in the IEEE 802-3 protocol as an example, if you want to get CRC circuit with 256-bit input data bus, the detailed instantiation code is as follows: +```verilog +CrcConfig#(32) conf = CrcConfig { + polynominal: 32'h04C11DB7, + initVal : 32'hFFFFFFFF, + finalXor : 32'hFFFFFFFF, + revInput : BIT_ORDER_REVERSE, + revOutput : BIT_ORDER_REVERSE, + memFilePrefix: "mem_tab", + crcMode: CRC_MODE_SEND +}; +CrcAxiStream#(32, 256) crc <- mkCrcAxiStream(conf); +``` + +5. Generate lookup table files: The script for generating lookup table files is **scripts/gen_crc_tab.py**. Before using this script, you need to specify CRC configuration in .json file, whose content must be consistent with the configuration in BSV code: +```json +{ + "crc_width": 32, + "axi_keep_width": 32, + "polynomial": "0x04C11DB7", + "init_value": "0xFFFFFFFF", + "final_xor": "0xFFFFFFFF", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_SEND" +} +``` + +6. Once .json file is configured, run the script using python (you need to specify the path of JSON configuration file and output directory) to get lookup table files: +```shell +python3 gen_crc_tab.py JSON_FILE_PATH OUTPUT_DIR +``` + +7. When compiling the project, it is necessary to add the path of blue-crc source code to compile options. Assuming that the root directory of blue-crc is $(ROOT): +```shell +bsc -p +:$(BLUE_CRC)/src:$(ROOT)/lib/blue-wrapper/src ``` -The generated files containing Verilog modules and contents of used lookup tables are located in the [gen/](./gen/) directory. It shound be noted that you need to install [bluespec compiler](https://github.com/B-Lang-org/bsc/releases) before running the scripts to generate Verilog files. -# Further Work -- Refine timing violation under the configuration of 512-bit dataWidth and 32-bit crcWidth. -- Complete documentation in algorithm theory part. \ No newline at end of file +### Verilog Interface +Although the blue-crc project is implemented using BSV, it also provides script **scripts/gen_crc.py** to generate configurable Verilog codes. The script needs to be executed in the root directory of blue-crc project, and the path of CRC configuration file in .json format needs to be specified: +```shell +python3 scripts/gen_crc.py JSON_FILE_PATH [OUTPUT_VERILOG_DIR] [OUTPUT_TABLE_DIR] +``` +If the output directories of Verilog codes and lookup tables are not configured when executing the script, these files will be generated to **verilog** folder under the root directory by default. Generating Verilog codes requires [BSV compiler](https://github.com/B-Lang-org/bsc#download), so you need to ensure that the compiler is installed and configured before executing the script. diff --git a/backend/vivado/Makefile b/backend/vivado/Makefile index a0a33cf..2100850 100644 --- a/backend/vivado/Makefile +++ b/backend/vivado/Makefile @@ -1,68 +1,63 @@ -PWD = $(shell pwd) -BUILDDIR = build -SRCDIR = ../../src -VLOGDIR = generated +ROOT_DIR = $(abspath ../../) +SCRIPTS_DIR = $(ROOT_DIR)/scripts +include $(SCRIPTS_DIR)/Makefile.base +LOCALSRCDIR = $(ROOT_DIR)/src +LIBSRCDIR = $(ROOT_DIR)/lib/blue-wrapper/src + + +FILE = CrcAxiStreamCustom.bsv +TOPMODULE = mkCrcRawAxiStreamCustom +TABDIR = . +VLOGDIR = verilog +VLOGFILE = $(VLOGDIR)/$(TOPMODULE).v +LIST_VERILOG_TCL = $(SCRIPTS_DIR)/listVlogFiles.tcl + +# CRC Configurations +JSON_CONF_FILE = $(SCRIPTS_DIR)/config/crc_32_256_recv.json +CRC_WIDTH = 32 +AXI_KEEP_WIDTH = 32 +POLY = 79764919 +INIT_VAL = 4294967295 +FINAL_XOR = 4294967295 +REV_INPUT = BIT_ORDER_REVERSE +REV_OUTPUT = BIT_ORDER_REVERSE +MEM_FILE_PREFIX = crc_tab +CRC_MODE = CRC_MODE_RECV + +MACROFLAGS = -D CRC_WIDTH=$(CRC_WIDTH) \ + -D AXI_KEEP_WIDTH=$(AXI_KEEP_WIDTH) \ + -D POLY=$(POLY) \ + -D INIT_VAL=$(INIT_VAL) \ + -D FINAL_XOR=$(FINAL_XOR) \ + -D REV_INPUT=$(REV_INPUT) \ + -D REV_OUTPUT=$(REV_OUTPUT) \ + -D MEM_FILE_PREFIX="\"$(MEM_FILE_PREFIX)\"" \ + -D CRC_MODE=$(CRC_MODE) + +# vivado config +XDCDIR = $(shell pwd) OUTPUTDIR = output +SUB_OUTPUTDIR = ONLYSYNTH = 0 CLK = main_clock -GEN_TAB = $(SRCDIR)/gen_crc_tab.py -LIST_VLOG = ../../scripts/listVlogFiles.tcl - -CRC_WIDTH ?= 8 -AXI_WIDTH ?= 64 -TARGET = Crc$(CRC_WIDTH)AxiStream$(AXI_WIDTH) -TARGETFILE = $(SRCDIR)/Crc$(CRC_WIDTH)AxiStream.bsv -TOPMODULE = mk$(TARGET) - -SUB_VLOGDIR = $(VLOGDIR)/$(TARGET) -SUB_OUTPUTDIR = $(OUTPUTDIR)/$(TARGET) export TOP = $(TOPMODULE) -export RTL = $(SUB_VLOGDIR) -export XDC = $(PWD) -export OUTPUT = $(SUB_OUTPUTDIR) +export RTL = $(VLOGDIR) +export XDC = $(XDCDIR) +export OUTPUT = $(OUTPUTDIR)/$(SUB_OUTPUTDIR) export SYNTHONLY = $(ONLYSYNTH) export CLOCKS = $(CLK) -TRANSFLAGS = -aggressive-conditions # -lift -split-if -RECOMPILEFLAGS = -u -show-compiles -SCHEDFLAGS = -show-schedule -sched-dot # -show-rule-rel dMemInit_request_put doExecute -# -show-elab-progress -DEBUGFLAGS = -check-assert \ - -continue-after-errors \ - -keep-fires \ - -keep-inlined-boundaries \ - -show-method-bvi \ - -show-method-conf \ - -show-module-use \ - -show-range-conflict \ - -show-stats \ - -warn-action-shadowing \ - -warn-method-urgency \ -# -promote-warnings ALL -VERILOGFLAGS = -verilog -remove-dollar -remove-unused-modules # -use-dpi -verilog-filter cmd -BLUESIMFLAGS = -parallel-sim-link 16 # -systemc -OUTDIR = -bdir $(BUILDDIR) -info-dir $(BUILDDIR) -simdir $(BUILDDIR) -vdir $(BUILDDIR) -WORKDIR = -fdir $(abspath .) -BSVSRCDIR = -p +:$(abspath $(SRCDIR)) -DIRFLAGS = $(BSVSRCDIR) $(OUTDIR) $(WORKDIR) -MISCFLAGS = -print-flags -show-timestamps -show-version # -steps 1000000000000000 -D macro -RUNTIMEFLAGS = +RTS -K256M -RTS -SIMEXE = $(BUILDDIR)/out +table: + mkdir -p $(TABDIR) + python3 $(SCRIPTS_DIR)/gen_crc_tab.py $(JSON_CONF_FILE) $(TABDIR) -compile: +verilog: table mkdir -p $(BUILDDIR) - bsc -elab -sim -verbose $(BLUESIMFLAGS) $(DEBUGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) -g $(TOPMODULE) $(TARGETFILE) - -link: compile - bsc -sim $(BLUESIMFLAGS) $(DIRFLAGS) $(RECOMPILEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) -e $(TOPMODULE) -o $(SIMEXE) - - -verilog: link - bsc $(VERILOGFLAGS) $(DIRFLAGS) $(RECOMPILEFLAGS) $(TRANSFLAGS) -g $(TOPMODULE) $(TARGETFILE) - mkdir -p $(SUB_VLOGDIR) - bluetcl $(LIST_VLOG) -bdir $(BUILDDIR) -vdir $(BUILDDIR) $(TOPMODULE) $(TOPMODULE) | grep -i '\.v' | xargs -I {} cp {} $(SUB_VLOGDIR) - python3 $(GEN_TAB) $(CRC_WIDTH) $(AXI_WIDTH) + bsc -elab $(VERILOGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(TRANSFLAGS) $(MACROFLAGS) -g $(TOPMODULE) $(LOCALSRCDIR)/$(FILE) + mkdir -p $(VLOGDIR) + echo "" > $(VLOGFILE) + bluetcl $(LIST_VERILOG_TCL) -bdir $(BUILDDIR) -vdir $(BUILDDIR) $(TOPMODULE) $(TOPMODULE) | grep -i '\.v' | xargs -I {} cat {} >> $(VLOGFILE) vivado: verilog vivado -mode batch -source non_project_build.tcl 2>&1 | tee ./run.log @@ -70,5 +65,5 @@ vivado: verilog clean: rm -rf $(BUILDDIR) $(VLOGDIR) $(OUTPUTDIR) .Xil *.jou *.log *.mem -.PHONY: compile link clean vivado -.DEFAULT_GOAL := vivado \ No newline at end of file +.PHONY: table compile verilog clean vivado +.DEFAULT_GOAL := vivado diff --git a/backend/vivado/run_vivado.py b/backend/vivado/run_vivado.py new file mode 100644 index 0000000..5606095 --- /dev/null +++ b/backend/vivado/run_vivado.py @@ -0,0 +1,67 @@ +import os +import sys +import json +from functools import reduce + + +def run_vivado(conf_file_path): + with open(conf_file_path) as json_file: + crc_config = json.load(json_file) + + crc_width = crc_config["crc_width"] + axi_keep_width = crc_config["axi_keep_width"] + polynomial = int(crc_config["polynomial"], 16) + init_value = int(crc_config["init_value"], 16) + final_xor = int(crc_config["final_xor"], 16) + reverse_input = crc_config["reverse_input"] + + if reverse_input: + reverse_input = "BIT_ORDER_REVERSE" + else: + reverse_input = "BIT_ORDER_NOT_REVERSE" + + reverse_output = crc_config["reverse_output"] + if reverse_output: + reverse_output = "BIT_ORDER_REVERSE" + else: + reverse_output = "BIT_ORDER_NOT_REVERSE" + + mem_file_prefix = crc_config["mem_file_prefix"] + crc_mode = crc_config["crc_mode"] + conf_file_prefix = os.path.basename(conf_file_path) + conf_file_prefix = os.path.splitext(conf_file_prefix)[0] + print( + f"Run Vivado on: crc_width={crc_width} axi_keep_width={axi_keep_width} crc_mode={crc_mode}" + ) + + macros = [f"JSON_CONF_FILE={conf_file_path}"] + macros.append(f"CRC_WIDTH={crc_width}") + macros.append(f"AXI_KEEP_WIDTH={axi_keep_width}") + macros.append(f"POLY={polynomial}") + macros.append(f"INIT_VAL={init_value}") + macros.append(f"FINAL_XOR={final_xor}") + macros.append(f"REV_INPUT={reverse_input}") + macros.append(f"REV_OUTPUT={reverse_output}") + macros.append(f"MEM_FILE_PREFIX={mem_file_prefix}") + macros.append(f"CRC_MODE={crc_mode}") + macros.append(f"SUB_OUTPUTDIR={conf_file_prefix}") + + make_args = reduce(lambda x, y: x + " " + y, macros) + os.system("rm -rf build verilog *.mem") + result = os.system(f"make {make_args}") + return result + + +if __name__ == "__main__": + if len(sys.argv) == 2: + conf_file_path = sys.argv[1] + conf_file_path = os.path.abspath(conf_file_path) + run_vivado(conf_file_path) + else: + config_dir = os.path.abspath("../../scripts/config") + for root, dirs, files in os.walk(config_dir): + for file_name in files: + conf_file_path = os.path.join(root, file_name) + result = run_vivado(conf_file_path) + info = f"Run Vivado Failed on the configuration of {file_name}." + assert result == 0, info diff --git a/backend/vivado/run_vivado.sh b/backend/vivado/run_vivado.sh deleted file mode 100755 index a6eb0e3..0000000 --- a/backend/vivado/run_vivado.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -crc_width_opt=(8 16 32) -axi_width_opt=(64 128 256 512) - -mkdir -p ${log_dir} - -for crc_width in ${crc_width_opt[@]}; do - for axi_width in ${axi_width_opt[@]}; do - make vivado CRC_WIDTH=${crc_width} AXI_WIDTH=${axi_width} - rm -f *.mem - done -done diff --git a/img/blockdiagram.png b/img/blockdiagram.png index b445f12..b75f51b 100644 Binary files a/img/blockdiagram.png and b/img/blockdiagram.png differ diff --git a/img/transaction.png b/img/transaction.png index 2e4a4a0..3b7a1bc 100644 Binary files a/img/transaction.png and b/img/transaction.png differ diff --git a/lib/blue-wrapper b/lib/blue-wrapper new file mode 160000 index 0000000..a0d0fcc --- /dev/null +++ b/lib/blue-wrapper @@ -0,0 +1 @@ +Subproject commit a0d0fccccaa6b64bfae14f317773d08888d527a6 diff --git a/run.sh b/run.sh index 03a46a7..9910f82 100755 --- a/run.sh +++ b/run.sh @@ -18,12 +18,7 @@ COCOTB_DIR=${TEST_DIR}/cocotb echo -e "\nStart formatting Codes" black --check $(find ./ -name "*.py") -# Run Bluesim Tests -echo -e "\nStart Bluesim Tests" -cd ${BLUESIM_DIR} -source ./run_tests.sh - # Run Cocotb Tests -echo -e "\nStart Cocotb Tests" +echo -e "\nStart Cocotb Testbenches" cd ${COCOTB_DIR} -source ./run_tests.sh +python3 run_tests.py diff --git a/scripts/CrcGenerator.py b/scripts/CrcGenerator.py deleted file mode 100644 index 6a56f82..0000000 --- a/scripts/CrcGenerator.py +++ /dev/null @@ -1,115 +0,0 @@ -import os -import sys -from functools import reduce - -sys.path.append("./src") -from gen_crc_tab import CrcLookUpTable - -CRC_WIDTH = 32 -INPUT_WIDTH = 256 -POLYNOMINAL = 0x04C11DB7 -INIT_VALUE = 0xFFFFFFFF -FINAL_XOR = 0xFFFFFFFF -REFLECT_INPUT = True -REFLECT_OUTPUT = True - - -class CrcAxiStreamConfig: - def __init__( - self, - crc_width: int, - input_width: int, - poly: int, - init_val: int, - final_xor: int, - reflect_in: bool, - reflect_out: bool, - ): - assert crc_width % 8 == 0, "crc_width must be multiples of 8-bit" - assert input_width % 8 == 0, "input_width must be multiples of 8-bit" - assert (poly >= 0) & (poly < pow(2, crc_width)), "polynominal out of range" - assert (init_val >= 0) & ( - init_val < pow(2, crc_width) - ), "initial value out of range" - assert (final_xor >= 0) & ( - final_xor < pow(2, crc_width) - ), "final xor out of range" - - self.crc_width = crc_width - self.crc_byte_width = int(crc_width / 8) - self.input_width = input_width - self.input_byte_width = int(input_width / 8) - self.poly = poly - self.init_val = init_val - self.final_xor = final_xor - self.reflect_in = reflect_in - self.reflect_out = reflect_out - - def gen_table(self): - root_path = test_dir = os.path.abspath(".") - gen_path = os.path.join(root_path, "gen") - file_prefix = os.path.join(gen_path, "crc_tab") - - tab_init_val = 0 - tab_final_xor = 0 - crc_tab = CrcLookUpTable( - self.poly.to_bytes(self.crc_byte_width, "big"), - tab_init_val.to_bytes(self.crc_byte_width, "big"), - tab_final_xor.to_bytes(self.crc_byte_width, "big"), - False, - False, - ) - - os.system("mkdir -p gen") - crc_tab.gen_crc_tab_file(file_prefix, range(self.input_byte_width)) - - def gen_verilog(self): - root_path = test_dir = os.path.abspath(".") - src_path = os.path.join(root_path, "src") - scripts_path = os.path.join(root_path, "scripts") - gen_path = os.path.join(root_path, "gen") - bsv_file = "CrcRawAxiStreamCustom.bsv" - module = "mkCrcRawAxiStreamCustom" - makefile = os.path.join(scripts_path, "GenVerilog.mk") - - macros = [f"CRC_WIDTH={self.crc_width}"] - macros.append(f"KEEP_WIDTH={self.input_byte_width}") - macros.append(f"DATA_WIDTH={self.input_width}") - macros.append(f"POLY={self.poly}") - macros.append(f"INIT_VAL={self.init_val}") - macros.append(f"FINAL_XOR={self.final_xor}") - macros.append(f"REFLECT_IN={self.reflect_in}") - macros.append(f"REFLECT_OUT={self.reflect_out}") - macro_args = list(map(lambda x: "-D " + x, macros)) - macro_args = reduce(lambda x, y: x + " " + y, macro_args) - macro_args = '"' + macro_args + '"' - - make_args = [f"CRC_WIDTH={self.crc_width}"] - make_args.append(f"AXI_WIDTH={self.input_width}") - make_args.append(f"SCRIPTS_PATH={scripts_path}") - make_args.append(f"MACROSAGS={macro_args}") - make_args.append(f"FILE={bsv_file}") - make_args.append(f"TOP={module}") - make_args.append(f"FILE_PATH={src_path}") - make_args.append(f"VLOGDIR={gen_path}") - make_args.append(f"TABDIR={gen_path}") - make_args.append(f'BSVSRCDIR="-p +:{src_path}"') - make_args = reduce(lambda x, y: x + " " + y, make_args) - - os.system("mkdir -p gen") - os.system(f"make -f {makefile} verilog {make_args}") - os.system("rm -rf build") - - -if __name__ == "__main__": - crcConf = CrcAxiStreamConfig( - CRC_WIDTH, - INPUT_WIDTH, - POLYNOMINAL, - INIT_VALUE, - FINAL_XOR, - REFLECT_INPUT, - REFLECT_OUTPUT, - ) - crcConf.gen_verilog() - crcConf.gen_table() diff --git a/scripts/GenVerilog.mk b/scripts/GenVerilog.mk deleted file mode 100644 index 91967e6..0000000 --- a/scripts/GenVerilog.mk +++ /dev/null @@ -1,23 +0,0 @@ -include $(SCRIPTS_PATH)/Makefile.base - -SCRIPTS_PATH ?= -CRC_WIDTH ?= -AXI_WIDTH ?= -FILE ?= -TOP ?= -FILE_PATH ?= -VLOGDIR ?= -TABDIR ?= -TARGET_FILE = $(VLOGDIR)/$(TOP).v -LIST_VERILOG_TCL = $(SCRIPTS_PATH)/listVlogFiles.tcl - - -verilog: - mkdir -p $(BUILDDIR) - bsc -elab $(VERILOGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(TRANSFLAGS) $(MACROSAGS) -g $(TOP) $(FILE_PATH)/$(FILE) - mkdir -p $(VLOGDIR) - echo "" > $(TARGET_FILE) - bluetcl $(LIST_VERILOG_TCL) -bdir $(BUILDDIR) -vdir $(BUILDDIR) $(TOP) $(TOP) | grep -i '\.v' | xargs -I {} cat {} >> $(TARGET_FILE) - - -.PHONY: verilog \ No newline at end of file diff --git a/scripts/Makefile.base b/scripts/Makefile.base index 69b135a..baf8303 100644 --- a/scripts/Makefile.base +++ b/scripts/Makefile.base @@ -1,4 +1,4 @@ -TRANSFLAGS = -aggressive-conditions # -lift -split-if +TRANSFLAGS = -aggressive-conditions -lift # -split-if RECOMPILEFLAGS = -u -show-compiles SCHEDFLAGS = -show-schedule -sched-dot # -show-rule-rel dMemInit_request_put doExecute # -show-elab-progress @@ -19,8 +19,9 @@ BLUESIMFLAGS = -parallel-sim-link 16 # -systemc BUILDDIR = build OUTDIR = -bdir $(BUILDDIR) -info-dir $(BUILDDIR) -simdir $(BUILDDIR) -vdir $(BUILDDIR) WORKDIR = -fdir $(abspath .) -BSVSRCDIR = -p +:$(abspath ../../src) +LOCALSRCDIR ?= +LIBSRCDIR ?= +BSVSRCDIR = -p +:$(LOCALSRCDIR):$(LIBSRCDIR) DIRFLAGS = $(BSVSRCDIR) $(OUTDIR) $(WORKDIR) -MISCFLAGS = -print-flags -show-timestamps -show-version -steps 2000000 # -D macro +MISCFLAGS = -print-flags -show-timestamps -show-version -steps 6000000 # -D macro RUNTIMEFLAGS = +RTS -K4095M -RTS -MACROSAGS ?= diff --git a/scripts/config/crc_16_256_send.json b/scripts/config/crc_16_256_send.json new file mode 100644 index 0000000..871b047 --- /dev/null +++ b/scripts/config/crc_16_256_send.json @@ -0,0 +1,11 @@ +{ + "crc_width": 16, + "axi_keep_width": 32, + "polynomial": "0x8005", + "init_value": "0x0000", + "final_xor": "0x0000", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_SEND" +} \ No newline at end of file diff --git a/scripts/config/crc_32_128_send.json b/scripts/config/crc_32_128_send.json new file mode 100644 index 0000000..4ffa63c --- /dev/null +++ b/scripts/config/crc_32_128_send.json @@ -0,0 +1,11 @@ +{ + "crc_width": 32, + "axi_keep_width": 16, + "polynomial": "0x04C11DB7", + "init_value": "0xFFFFFFFF", + "final_xor": "0xFFFFFFFF", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_SEND" +} \ No newline at end of file diff --git a/scripts/config/crc_32_248_recv.json b/scripts/config/crc_32_248_recv.json new file mode 100644 index 0000000..1012d9e --- /dev/null +++ b/scripts/config/crc_32_248_recv.json @@ -0,0 +1,11 @@ +{ + "crc_width": 32, + "axi_keep_width": 31, + "polynomial": "0x04C11DB7", + "init_value": "0xFFFFFFFF", + "final_xor": "0xFFFFFFFF", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_RECV" +} \ No newline at end of file diff --git a/scripts/config/crc_32_256_recv.json b/scripts/config/crc_32_256_recv.json new file mode 100644 index 0000000..e4129ab --- /dev/null +++ b/scripts/config/crc_32_256_recv.json @@ -0,0 +1,11 @@ +{ + "crc_width": 32, + "axi_keep_width": 32, + "polynomial": "0x04C11DB7", + "init_value": "0xFFFFFFFF", + "final_xor": "0xFFFFFFFF", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_RECV" +} \ No newline at end of file diff --git a/scripts/config/crc_32_256_send.json b/scripts/config/crc_32_256_send.json new file mode 100644 index 0000000..533d56b --- /dev/null +++ b/scripts/config/crc_32_256_send.json @@ -0,0 +1,11 @@ +{ + "crc_width": 32, + "axi_keep_width": 32, + "polynomial": "0x04C11DB7", + "init_value": "0xFFFFFFFF", + "final_xor": "0xFFFFFFFF", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_SEND" +} \ No newline at end of file diff --git a/scripts/config/crc_8_256_send.json b/scripts/config/crc_8_256_send.json new file mode 100644 index 0000000..d39f84e --- /dev/null +++ b/scripts/config/crc_8_256_send.json @@ -0,0 +1,11 @@ +{ + "crc_width": 8, + "axi_keep_width": 32, + "polynomial": "0x07", + "init_value": "0x00", + "final_xor": "0x00", + "reverse_input": true, + "reverse_output": true, + "mem_file_prefix": "crc_tab", + "crc_mode": "CRC_MODE_SEND" +} \ No newline at end of file diff --git a/scripts/gen_crc.py b/scripts/gen_crc.py new file mode 100644 index 0000000..20586c3 --- /dev/null +++ b/scripts/gen_crc.py @@ -0,0 +1,69 @@ +import os +import sys +import json +from functools import reduce + + +def gen_verilog(conf_file_path, verilog_dir, table_dir): + with open(conf_file_path) as json_file: + crc_config = json.load(json_file) + + crc_width = crc_config["crc_width"] + axi_keep_width = crc_config["axi_keep_width"] + polynomial = int(crc_config["polynomial"], 16) + init_value = int(crc_config["init_value"], 16) + final_xor = int(crc_config["final_xor"], 16) + reverse_input = crc_config["reverse_input"] + + if reverse_input: + reverse_input = "BIT_ORDER_REVERSE" + else: + reverse_input = "BIT_ORDER_NOT_REVERSE" + + reverse_output = crc_config["reverse_output"] + if reverse_output: + reverse_output = "BIT_ORDER_REVERSE" + else: + reverse_output = "BIT_ORDER_NOT_REVERSE" + + mem_file_prefix = crc_config["mem_file_prefix"] + crc_mode = crc_config["crc_mode"] + + root_dir = os.path.abspath(".") + + macros = [f"JSON_CONF_FILE={conf_file_path}"] + macros.append(f"CRC_WIDTH={crc_width}") + macros.append(f"AXI_KEEP_WIDTH={axi_keep_width}") + macros.append(f"POLY={polynomial}") + macros.append(f"INIT_VAL={init_value}") + macros.append(f"FINAL_XOR={final_xor}") + macros.append(f"REV_INPUT={reverse_input}") + macros.append(f"REV_OUTPUT={reverse_output}") + macros.append(f"MEM_FILE_PREFIX={mem_file_prefix}") + macros.append(f"CRC_MODE={crc_mode}") + macros.append(f"ROOT_DIR={root_dir}") + macros.append(f"VLOGDIR={verilog_dir}") + macros.append(f"TABDIR={table_dir}") + + make_args = reduce(lambda x, y: x + " " + y, macros) + makefile_path = f"{root_dir}/test/cocotb/Makefile" + os.system(f"rm -rf {verilog_dir} {table_dir}") + result = os.system(f"make verilog -f {makefile_path} {make_args}") + os.system(f"rm -rf build") + return result + + +if __name__ == "__main__": + assert ( + len(sys.argv) >= 2 + ), "Usage: python3 gen_crc.py JSON_CONFIG_FILE [VLOGDIR] [TABDIR]" + arg = sys.argv + json_file_path = arg[1] + verilog_dir = "verilog" + table_dir = "verilog" + if len(sys.argv) > 2: + verilog_dir = arg[2] + if len(sys.argv) > 3: + table_dir = arg[3] + result = gen_verilog(json_file_path, verilog_dir, table_dir) + assert result == 0, "The generation of CRC hardware failed." diff --git a/scripts/gen_crc_tab.py b/scripts/gen_crc_tab.py new file mode 100644 index 0000000..45786fa --- /dev/null +++ b/scripts/gen_crc_tab.py @@ -0,0 +1,159 @@ +import os +import sys +import json + + +def bits_reverse(val: int, width: int): + result = 0 + for i in range(width): + result <<= 1 + result |= val & 1 + val >>= 1 + return result + + +def bits_left_shift(val: int, width: int, amt: int): + mask = (1 << width) - 1 + return (val << amt) & mask + + +def bits_msb(val: int, width: int): + return val >> (width - 1) + + +class CrcCalculator: + def __init__( + self, + width: int, + polynomial: int, + init_value: int, + final_xor_value: int, + reverse_input: bool, + reverse_output: bool, + ): + self.width = width + self.byte_width = 8 + self.byte_num = int(width / self.byte_width) + assert ( + width % self.byte_width == 0 + ), f"The width of {width}-bits is not supported" + self.max_crc_value = pow(2, width) + + self.polynomial = polynomial + self.init_value = init_value + self.final_xor_value = final_xor_value + self.reverse_input = reverse_input + self.reverse_output = reverse_output + assert ( + polynomial < self.max_crc_value + ), "The value of polynominal is out of bound" + assert ( + init_value < self.max_crc_value + ), "The value of init_value is out of bound" + assert ( + final_xor_value < self.max_crc_value + ), "The value of final_xor_value is out of bound" + + def add_one_byte(self, byte_int: int): + if self.reverse_input: + byte_int = bits_reverse(byte_int, self.byte_width) + + byte_int = byte_int << (self.width - self.byte_width) + crc_result = self.crc_result + crc_result = crc_result ^ byte_int + + for i in range(self.byte_width): + if bits_msb(crc_result, self.width): + crc_result = bits_left_shift(crc_result, self.width, 1) + crc_result = crc_result ^ self.polynomial + else: + crc_result = bits_left_shift(crc_result, self.width, 1) + + self.crc_result = crc_result + + def get_crc_result(self, raw_data: bytes): + self.crc_result = self.init_value + + for byte in raw_data: + self.add_one_byte(byte) + + if self.reverse_output: + self.crc_result = bits_reverse(self.crc_result, self.width) + + self.crc_result = self.crc_result ^ self.final_xor_value + return self.crc_result + + +class CrcLookUpTabGenerator: + def __init__( + self, + crc_width: int, + polynomial: int, + max_byte_offset: int, + file_path: str, + file_prefix: str, + ): + self.crc_width = crc_width + self.byte_width = 8 + self.crc_byte_num = int(crc_width / self.byte_width) + self.max_byte_offset = max_byte_offset + self.file_path = file_path + self.file_prefix = file_prefix + self.crc_calculator = CrcCalculator( + width=crc_width, + polynomial=polynomial, + init_value=0, + final_xor_value=0, + reverse_input=False, + reverse_output=False, + ) + + def gen_crc_tab_for_one_byte(self, byte_offset: int): + crc_tab = [] + for i in range(pow(2, self.byte_width)): + if byte_offset < self.crc_byte_num: + crc_result = i << (self.byte_width * byte_offset) + else: + shift_amt = byte_offset - self.crc_byte_num + raw_data = i << (self.byte_width * (shift_amt)) + raw_data = raw_data.to_bytes(shift_amt + 1, "big") + crc_result = self.crc_calculator.get_crc_result(raw_data) + + crc_tab.append(crc_result) + + return crc_tab + + def gen_crc_tab_files(self): + for i in range(self.max_byte_offset): + file_name = os.path.join(self.file_path, self.file_prefix) + file_name = file_name + f"_{i}.mem" + file = open(file_name, "w") + crc_tab = self.gen_crc_tab_for_one_byte(i) + for crc in crc_tab: + file.write(hex(crc)[2:] + "\n") + file.close() + + +if __name__ == "__main__": + assert ( + len(sys.argv) == 3 + ), "Usage: python3 gen_crc_tab.py JSON_CONFIG_FILE TARGET_DIR" + + arg = sys.argv + json_file_path = arg[1] + output_path = arg[2] + + with open(json_file_path) as json_file: + crc_config = json.load(json_file) + + crc_width = crc_config["crc_width"] + polynomial = int(crc_config["polynomial"], 16) + axi_keep_width = crc_config["axi_keep_width"] + max_byte_offset = axi_keep_width + 4 + file_prefix = crc_config["mem_file_prefix"] + + tab_generator = CrcLookUpTabGenerator( + crc_width, polynomial, max_byte_offset, output_path, file_prefix + ) + + tab_generator.gen_crc_tab_files() diff --git a/src/Crc16AxiStream.bsv b/src/Crc16AxiStream.bsv deleted file mode 100644 index c03ae1d..0000000 --- a/src/Crc16AxiStream.bsv +++ /dev/null @@ -1,132 +0,0 @@ -import CrcAxiStream :: *; -import CrcRawAxiStream :: *; - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// -/// Implementation of the CRC-16-ANSI standard (x^16 + x^15 + x^2 + 1) -/// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -typedef CrcAxiStream#(CRC16_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc16AxiStream64; -(* synthesize *) -module mkCrc16AxiStream64(Crc16AxiStream64); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16AxiStream64 crc16 <- mkCrcAxiStream(conf); - return crc16; -endmodule -typedef CrcRawAxiStream#(CRC16_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc16RawAxiStream64; -(* synthesize *) -module mkCrc16RawAxiStream64(Crc16RawAxiStream64); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16RawAxiStream64 crc16 <- mkCrcRawAxiStream(conf); - return crc16; -endmodule - - -typedef CrcAxiStream#(CRC16_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc16AxiStream128; -(* synthesize *) -module mkCrc16AxiStream128(Crc16AxiStream128); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16AxiStream128 crc16 <- mkCrcAxiStream(conf); - return crc16; -endmodule - -typedef CrcRawAxiStream#(CRC16_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc16RawAxiStream128; -(* synthesize *) -module mkCrc16RawAxiStream128(Crc16RawAxiStream128); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16RawAxiStream128 crc16 <- mkCrcRawAxiStream(conf); - return crc16; -endmodule - - -typedef CrcAxiStream#(CRC16_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc16AxiStream256; -(* synthesize *) -module mkCrc16AxiStream256(Crc16AxiStream256); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16AxiStream256 crc16 <- mkCrcAxiStream(conf); - return crc16; -endmodule - -typedef CrcRawAxiStream#(CRC16_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc16RawAxiStream256; -(* synthesize *) -module mkCrc16RawAxiStream256(Crc16RawAxiStream256); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16RawAxiStream256 crc16 <- mkCrcRawAxiStream(conf); - return crc16; -endmodule - - -typedef CrcAxiStream#(CRC16_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc16AxiStream512; -(* synthesize *) -module mkCrc16AxiStream512(Crc16AxiStream512); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16AxiStream512 crc16 <- mkCrcAxiStream(conf); - return crc16; -endmodule - -typedef CrcRawAxiStream#(CRC16_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc16RawAxiStream512; -(* synthesize *) -module mkCrc16RawAxiStream512(Crc16RawAxiStream512); - CrcConfig#(CRC16_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc16RawAxiStream512 crc16 <- mkCrcRawAxiStream(conf); - return crc16; -endmodule diff --git a/src/Crc32AxiStream.bsv b/src/Crc32AxiStream.bsv deleted file mode 100644 index fe3bd50..0000000 --- a/src/Crc32AxiStream.bsv +++ /dev/null @@ -1,134 +0,0 @@ -import CrcAxiStream :: *; -import CrcRawAxiStream :: *; - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// -/// Implementation of the CRC-32 (IEEE 802.3) standard -/// (x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1) -/// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -typedef CrcAxiStream#(CRC32_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc32AxiStream64; -(* synthesize *) -module mkCrc32AxiStream64(Crc32AxiStream64); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32AxiStream64 crc32 <- mkCrcAxiStream(conf); - return crc32; -endmodule - -typedef CrcRawAxiStream#(CRC32_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc32RawAxiStream64; -(* synthesize *) -module mkCrc32RawAxiStream64(Crc32RawAxiStream64); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32RawAxiStream64 crc32 <- mkCrcRawAxiStream(conf); - return crc32; -endmodule - - -typedef CrcAxiStream#(CRC32_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc32AxiStream128; -(* synthesize *) -module mkCrc32AxiStream128(Crc32AxiStream128); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32AxiStream128 crc32 <- mkCrcAxiStream(conf); - return crc32; -endmodule - -typedef CrcRawAxiStream#(CRC32_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc32RawAxiStream128; -(* synthesize *) -module mkCrc32RawAxiStream128(Crc32RawAxiStream128); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32RawAxiStream128 crc32 <- mkCrcRawAxiStream(conf); - return crc32; -endmodule - - -typedef CrcAxiStream#(CRC32_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc32AxiStream256; -(* synthesize *) -module mkCrc32AxiStream256(Crc32AxiStream256); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32AxiStream256 crc32 <- mkCrcAxiStream(conf); - return crc32; -endmodule - -typedef CrcRawAxiStream#(CRC32_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc32RawAxiStream256; -(* synthesize *) -module mkCrc32RawAxiStream256(Crc32RawAxiStream256); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32RawAxiStream256 crc32 <- mkCrcRawAxiStream(conf); - return crc32; -endmodule - - -typedef CrcAxiStream#(CRC32_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc32AxiStream512; -(* synthesize *) -module mkCrc32AxiStream512(Crc32AxiStream512); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32AxiStream512 crc32 <- mkCrcAxiStream(conf); - return crc32; -endmodule - -typedef CrcRawAxiStream#(CRC32_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc32RawAxiStream512; -(* synthesize *) -module mkCrc32RawAxiStream512(Crc32RawAxiStream512); - CrcConfig#(CRC32_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - Crc32RawAxiStream512 crc32 <- mkCrcRawAxiStream(conf); - return crc32; -endmodule diff --git a/src/Crc8AxiStream.bsv b/src/Crc8AxiStream.bsv deleted file mode 100644 index 8fc9f70..0000000 --- a/src/Crc8AxiStream.bsv +++ /dev/null @@ -1,133 +0,0 @@ -import CrcAxiStream :: *; -import CrcRawAxiStream :: *; - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// -/// Implementation of the CRC-8-CCITT standard (x^8 + x^2 + x^1 + 1) -/// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -typedef CrcAxiStream#(CRC8_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc8AxiStream64; -(* synthesize *) -module mkCrc8AxiStream64(Crc8AxiStream64); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8AxiStream64 crc8 <- mkCrcAxiStream(conf); - return crc8; -endmodule - -typedef CrcRawAxiStream#(CRC8_WIDTH, AXIS64_KEEP_WIDTH, AXIS64_WIDTH) Crc8RawAxiStream64; -(* synthesize *) -module mkCrc8RawAxiStream64(Crc8RawAxiStream64); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8RawAxiStream64 crc8 <- mkCrcRawAxiStream(conf); - return crc8; -endmodule - - -typedef CrcAxiStream#(CRC8_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc8AxiStream128; -(* synthesize *) -module mkCrc8AxiStream128(Crc8AxiStream128); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8AxiStream128 crc8 <- mkCrcAxiStream(conf); - return crc8; -endmodule - -typedef CrcRawAxiStream#(CRC8_WIDTH, AXIS128_KEEP_WIDTH, AXIS128_WIDTH) Crc8RawAxiStream128; -(* synthesize *) -module mkCrc8RawAxiStream128(Crc8RawAxiStream128); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8RawAxiStream128 crc8 <- mkCrcRawAxiStream(conf); - return crc8; -endmodule - - -typedef CrcAxiStream#(CRC8_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc8AxiStream256; -(* synthesize *) -module mkCrc8AxiStream256(Crc8AxiStream256); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8AxiStream256 crc8 <- mkCrcAxiStream(conf); - return crc8; -endmodule - -typedef CrcRawAxiStream#(CRC8_WIDTH, AXIS256_KEEP_WIDTH, AXIS256_WIDTH) Crc8RawAxiStream256; -(* synthesize *) -module mkCrc8RawAxiStream256(Crc8RawAxiStream256); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8RawAxiStream256 crc8 <- mkCrcRawAxiStream(conf); - return crc8; -endmodule - - -typedef CrcAxiStream#(CRC8_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc8AxiStream512; -(* synthesize *) -module mkCrc8AxiStream512(Crc8AxiStream512); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8AxiStream512 crc8 <- mkCrcAxiStream(conf); - return crc8; -endmodule - -typedef CrcRawAxiStream#(CRC8_WIDTH, AXIS512_KEEP_WIDTH, AXIS512_WIDTH) Crc8RawAxiStream512; -(* synthesize *) -module mkCrc8RawAxiStream512(Crc8RawAxiStream512); - CrcConfig#(CRC8_WIDTH) conf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - Crc8RawAxiStream512 crc8 <- mkCrcRawAxiStream(conf); - return crc8; -endmodule diff --git a/src/CrcAxiStream.bsv b/src/CrcAxiStream.bsv index c577568..535fca0 100644 --- a/src/CrcAxiStream.bsv +++ b/src/CrcAxiStream.bsv @@ -1,252 +1,192 @@ import FIFOF :: *; -import RegFile :: *; import Vector :: *; -import Printf :: *; import GetPut :: *; +import RegFile :: *; -// Parameters that define a specific CRC calculator -// input data width (8b bit) -// crc width (8n bit) -// polynominal -// final xor -// reflect input data -// reflect remainder - -//////////////////////////////////////////////////////////////////////////////// -////////// Definition of some common types -//////////////////////////////////////////////////////////////////////////////// -typedef 8 BYTE_WIDTH; -typedef Bit#(BYTE_WIDTH) Byte; - -typedef Bit#(width) CrcResult#(numeric type width); - -typedef struct { - Bit#(dataWidth) tData; - Bit#(keepWidth) tKeep; - Bool tUser; - Bool tLast; -} AxiStream#(numeric type keepWidth, numeric type dataWidth) deriving(Bits, Eq, FShow); - -typedef struct { - Bit#(width) polynominal; - Bit#(width) initVal; - Bit#(width) finalXor; - Bool reflectData; - Bool reflectRemainder; -} CrcConfig#(numeric type width) deriving(Bits, Eq, FShow); - -typedef 8 CRC8_WIDTH; -typedef 16 CRC16_WIDTH; -typedef 32 CRC32_WIDTH; - -typedef 64 AXIS64_WIDTH; -typedef 128 AXIS128_WIDTH; -typedef 256 AXIS256_WIDTH; -typedef 512 AXIS512_WIDTH; - -typedef TDiv#(AXIS64_WIDTH, BYTE_WIDTH) AXIS64_KEEP_WIDTH; -typedef TDiv#(AXIS128_WIDTH, BYTE_WIDTH) AXIS128_KEEP_WIDTH; -typedef TDiv#(AXIS256_WIDTH, BYTE_WIDTH) AXIS256_KEEP_WIDTH; -typedef TDiv#(AXIS512_WIDTH, BYTE_WIDTH) AXIS512_KEEP_WIDTH; - -typedef 8'h07 CRC8_CCITT_POLY; -typedef 16'h8005 CRC16_ANSI_POLY; -typedef 32'h04C11DB7 CRC32_IEEE_POLY; - -typedef 8'h00 CRC8_CCITT_INIT_VAL; -typedef 16'h0000 CRC16_ANSI_INIT_VAL; -typedef 32'hFFFFFFFF CRC32_IEEE_INIT_VAL; - -typedef 8'h00 CRC8_CCITT_FINAL_XOR; -typedef 16'h0000 CRC16_ANSI_FINAL_XOR; -typedef 32'hFFFFFFFF CRC32_IEEE_FINAL_XOR; - - -//////////////////////////////////////////////////////////////////////////////// -////////// Implementation of utility functions used in the design -//////////////////////////////////////////////////////////////////////////////// -module mkCrcRegFileTable#(Integer offset)(Integer idx, RegFile#(Byte, CrcResult#(width)) ifc); - // $display("crc lookup tab offset: %d", offset); - let initFile = sprintf("crc_tab_%d.mem", offset + idx); - RegFile#(Byte, CrcResult#(width)) regFile <- mkRegFileFullLoad(initFile); - return regFile; -endmodule +import CrcUtils :: *; +import CrcDefines :: *; -function CrcResult#(width) addCrc(CrcResult#(width) crc1, CrcResult#(width) crc2); - return crc1 ^ crc2; -endfunction - -function Bit#(width) swapEndian(Bit#(width) data) provisos(Mul#(BYTE_WIDTH, byteNum, width)); - Vector#(byteNum, Byte) dataVec = unpack(data); - return pack(reverse(dataVec)); -endfunction - -function Bit#(width) reverseBitsOfEachByte(Bit#(width) data) provisos(Mul#(BYTE_WIDTH, byteNum, width)); - Vector#(byteNum, Byte) dataVec = unpack(data); - Vector#(byteNum, Byte) revDataVec = map(reverseBits, dataVec); - return pack(revDataVec); -endfunction - -function Bit#(width) byteRightShift(Bit#(width) dataIn, Bit#(shiftAmtWidth) shiftAmt) - provisos(Mul#(BYTE_WIDTH, byteNum, width)); - Vector#(byteNum, Byte) dataInVec = unpack(dataIn); - dataInVec = shiftOutFrom0(0, dataInVec, shiftAmt); - return pack(dataInVec); -endfunction - -function Bit#(width) byteLeftShift(Bit#(width) dataIn, Bit#(shiftAmtWidth) shiftAmt) - provisos(Mul#(BYTE_WIDTH, byteNum, width)); - - Vector#(byteNum, Byte) dataInVec = unpack(dataIn); - dataInVec = shiftOutFromN(0, dataInVec, shiftAmt); - return pack(dataInVec); -endfunction - -typeclass ReduceBalancedTree#(numeric type num, type dType); - function dType reduceBalancedTree(function dType op(dType a, dType b), Vector#(num, dType) vecIn); -endtypeclass - -instance ReduceBalancedTree#(2, dType); - function reduceBalancedTree(op, vecIn) = op(vecIn[0], vecIn[1]); -endinstance - -instance ReduceBalancedTree#(1, dType); - function reduceBalancedTree(op, vecIn) = vecIn[0]; -endinstance - -instance ReduceBalancedTree#(num, dType) - provisos(Div#(num, 2, firstHalf), Add#(firstHalf, secondHalf, num), - ReduceBalancedTree#(firstHalf, dType), ReduceBalancedTree#(secondHalf, dType)); - function reduceBalancedTree(op, vecIn); - Vector#(firstHalf, dType) firstHalfVec = take(vecIn); - Vector#(secondHalf, dType) secondHalfVec = drop(vecIn); - let firstHalfRes = reduceBalancedTree(op, firstHalfVec); - let secondHalfRes = reduceBalancedTree(op, secondHalfVec); - return op(firstHalfRes, secondHalfRes); - endfunction -endinstance +import SemiFifo :: *; +import BusConversion :: *; +import AxiStreamTypes :: *; //////////////////////////////////////////////////////////////////////////////// ////////// Definitions of signals passed through pipelines //////////////////////////////////////////////////////////////////////////////// typedef struct { - Bool tLast; - Bit#(TLog#(TAdd#(1, byteWidth))) shiftAmt; -} CrcCtrlSig#(numeric type byteWidth) deriving(Bits, FShow); + Bool isLast; + Bool isFirst; + Bit#(TLog#(TAdd#(1, axiKeepWidth))) shiftAmt; +} CrcCtrlSig#(numeric type axiKeepWidth) deriving(Bits, FShow); typedef struct { - Bit#(width) data; - CrcCtrlSig#(byteNum) ctrlSig; -} PreProcessRes#(numeric type byteNum, numeric type width) deriving(Bits, FShow); + Bit#(TMul#(axiKeepWidth, BYTE_WIDTH)) data; + CrcCtrlSig#(axiKeepWidth) ctrlSig; +} PreProcessRes#(numeric type axiKeepWidth) deriving(Bits, FShow); -typedef PreProcessRes#(byteNum, width) ShiftInputRes#(numeric type byteNum, numeric type width); +typedef PreProcessRes#(axiKeepWidth) ShiftInputRes#(numeric type axiKeepWidth); + +typedef struct { + Vector#(axiKeepWidth, CrcResult#(crcWidth)) tempCrcVec; + CrcCtrlSig#(axiKeepWidth) ctrlSig; +} ReadCrcTabRes#(numeric type axiKeepWidth, numeric type crcWidth) deriving(Bits, FShow); typedef struct { CrcResult#(crcWidth) crcRes; - CrcCtrlSig#(dataByteNum) ctrlSig; -} ReduceCrcRes#(numeric type dataByteNum, numeric type crcWidth) deriving(Bits, FShow); + CrcCtrlSig#(axiKeepWidth) ctrlSig; +} ReduceCrcRes#(numeric type axiKeepWidth, numeric type crcWidth) deriving(Bits, FShow); typedef struct { CrcResult#(crcWidth) curCrc; CrcResult#(crcWidth) interCrc; - CrcCtrlSig#(dataByteNum) ctrlSig; -} AccumulateRes#(numeric type dataByteNum, numeric type crcWidth) deriving(Bits, FShow); + CrcCtrlSig#(axiKeepWidth) ctrlSig; +} AccuCrcRes#(numeric type axiKeepWidth, numeric type crcWidth) deriving(Bits, FShow); typedef struct { CrcResult#(crcWidth) curCrc; - CrcResult#(crcWidth) remainder; - Bit#(dataWidth) interCrc; -} ShiftInterCrcRes#(numeric type dataWidth, numeric type crcWidth) deriving(Bits, FShow); + Bit#(TAdd#(axiDataWidth, crcWidth)) interCrc; +} ShiftInterCrcRes#(numeric type axiDataWidth, numeric type crcWidth) deriving(Bits, FShow); +typedef struct { + Vector#(interByteNum, CrcResult#(crcWidth)) interCrc; + CrcResult#(crcWidth) curCrc; +} ReadInterCrcTabRes#(numeric type interByteNum, numeric type crcWidth) deriving(Bits, FShow); + + +module mkCrcAxiStreamPipeOut#( + CrcConfig#(crcWidth) conf, + AxiStreamPipeOut#(axiKeepWidth) crcReq +)(CrcResultPipeOut#(crcWidth)) provisos( + Mul#(BYTE_WIDTH, axiKeepWidth, axiDataWidth), + Mul#(BYTE_WIDTH, crcByteNum, crcWidth), + Mul#(BYTE_WIDTH, interByteNum, TAdd#(axiDataWidth, crcWidth)), + ReduceBalancedTree#(axiKeepWidth, CrcResult#(crcWidth)), + ReduceBalancedTree#(crcByteNum, CrcResult#(crcWidth)), + ReduceBalancedTree#(interByteNum, CrcResult#(crcWidth)) +); + + FIFOF#(PreProcessRes#(axiKeepWidth)) preProcessResBuf <- mkFIFOF; + FIFOF#(ShiftInputRes#(axiKeepWidth)) shiftInputResBuf <- mkFIFOF; + FIFOF#(ReadCrcTabRes#(axiKeepWidth, crcWidth)) readCrcTabResBuf <- mkFIFOF; + FIFOF#(ReduceCrcRes#(axiKeepWidth, crcWidth)) reduceCrcResBuf <- mkFIFOF; + FIFOF#(AccuCrcRes#(axiKeepWidth, crcWidth)) accuCrcResBuf <- mkFIFOF; + FIFOF#(ShiftInterCrcRes#(axiDataWidth, crcWidth)) shiftInterCrcResBuf <- mkFIFOF; + FIFOF#(ReadInterCrcTabRes#(interByteNum, crcWidth)) readInterCrcTabResBuf <- mkFIFOF; + FIFOF#(CrcResult#(crcWidth)) finalCrcResBuf <- mkFIFOF; + + Reg#(Bool) isFirstFlag <- mkReg(True); + Reg#(CrcResult#(crcWidth)) interCrcRes <- mkReg(conf.initVal); + Vector#(interByteNum, RegFile#(Byte, CrcResult#(crcWidth))) crcTabVec <- genWithM(mkCrcRegFileTable(0, conf.memFilePrefix)); + + rule preProcess; + let axiStream = crcReq.first; + crcReq.deq; + // swap endian + axiStream.tData = bitMask(axiStream.tData, axiStream.tKeep); + axiStream.tData = swapEndian(axiStream.tData); + axiStream.tKeep = reverseBits(axiStream.tKeep); + + // reverse bits of each input byte + if (conf.revInput == BIT_ORDER_REVERSE) begin + axiStream.tData = reverseBitsOfEachByte(axiStream.tData); + end -interface CrcAxiStream#(numeric type crcWidth, numeric type dataByteNum, numeric type dataWidth); - interface Put#(AxiStream#(dataByteNum, dataWidth)) crcReq; // crcReq - interface Get#(CrcResult#(crcWidth)) crcResp; // crcResp -endinterface + // TODO: modify count Zero logic + let extraByteNum = countZerosLSB(axiStream.tKeep); + + CrcCtrlSig#(axiKeepWidth) ctrlSig = CrcCtrlSig { + isFirst : isFirstFlag, + isLast : axiStream.tLast, + shiftAmt: pack(extraByteNum) + }; + PreProcessRes#(axiKeepWidth) preProcessRes = PreProcessRes { + data: axiStream.tData, + ctrlSig: ctrlSig + }; -module mkCrcAxiStream#(CrcConfig#(crcWidth) conf)(CrcAxiStream#(crcWidth, dataByteNum, dataWidth)) - provisos( - Mul#(BYTE_WIDTH, dataByteNum, dataWidth), - Mul#(BYTE_WIDTH, crcByteNum, crcWidth), - Mul#(BYTE_WIDTH, interByteNum, TAdd#(dataWidth, crcWidth)), - ReduceBalancedTree#(dataByteNum, CrcResult#(crcWidth)), - ReduceBalancedTree#(crcByteNum, CrcResult#(crcWidth)) - ); - - FIFOF#(PreProcessRes#(dataByteNum, dataWidth)) preProcessBuf <- mkFIFOF; - FIFOF#(ShiftInputRes#(dataByteNum, dataWidth)) shiftInputBuf <- mkFIFOF; - FIFOF#(Vector#(dataByteNum, CrcResult#(crcWidth))) readTabBuf <- mkFIFOF; - FIFOF#(CrcCtrlSig#(dataByteNum)) ctrlSigBuf <- mkFIFOF; - FIFOF#(ReduceCrcRes#(dataByteNum, crcWidth)) reduceCrcBuf <- mkFIFOF; - FIFOF#(AccumulateRes#(dataByteNum, crcWidth)) accuCrcBuf <- mkFIFOF; - FIFOF#(ShiftInterCrcRes#(dataWidth, crcWidth)) shiftInterBuf <- mkFIFOF; - FIFOF#(Vector#(dataByteNum, CrcResult#(crcWidth))) interReadTabBuf <- mkFIFOF; - FIFOF#(CrcResult#(crcWidth)) currentCrcBuf <- mkFIFOF; - FIFOF#(CrcResult#(crcWidth)) finalCrcBuf <- mkFIFOF; + isFirstFlag <= axiStream.tLast; + preProcessResBuf.enq(preProcessRes); + endrule - Reg#(CrcResult#(crcWidth)) interCrcRes <- mkReg(conf.initVal); - Vector#(dataByteNum, RegFile#(Byte, CrcResult#(crcWidth))) crcTabVec <- genWithM(mkCrcRegFileTable(0)); - Integer tabOffset = valueOf(dataByteNum) - valueOf(crcByteNum); - Vector#(crcByteNum, RegFile#(Byte, CrcResult#(crcWidth))) interCrcTabVec <- genWithM(mkCrcRegFileTable(tabOffset)); - rule shiftInput; - let preProcessRes = preProcessBuf.first; - preProcessBuf.deq; + let preProcessRes = preProcessResBuf.first; + preProcessResBuf.deq; let data = preProcessRes.data; let shiftAmt = preProcessRes.ctrlSig.shiftAmt; preProcessRes.data = byteRightShift(data, shiftAmt); - shiftInputBuf.enq(preProcessRes); + shiftInputResBuf.enq(preProcessRes); + //$display("shiftInput Result: %x", preProcessRes.data); endrule - rule readCrcTable; - let shiftInputRes = shiftInputBuf.first; - shiftInputBuf.deq; - Vector#(dataByteNum, Byte) dataVec = unpack(shiftInputRes.data); - Vector#(dataByteNum, CrcResult#(crcWidth)) tempCrcVec = newVector; - for (Integer i = 0; i < valueOf(dataByteNum); i = i + 1) begin - tempCrcVec[i] = crcTabVec[i].sub(dataVec[i]); + rule readCrcTab; + let shiftInputRes = shiftInputResBuf.first; + shiftInputResBuf.deq; + Vector#(axiKeepWidth, Byte) dataVec = unpack(shiftInputRes.data); + Vector#(axiKeepWidth, CrcResult#(crcWidth)) tempCrcVec = newVector; + Integer tabOffset = 0; + if (conf.crcMode == CRC_MODE_SEND) tabOffset = valueOf(crcByteNum); + for (Integer i = 0; i < valueOf(axiKeepWidth); i = i + 1) begin + tempCrcVec[i] = crcTabVec[tabOffset + i].sub(dataVec[i]); + //$display("read tab %d result: %x", i, tempCrcVec[i]); end - readTabBuf.enq(tempCrcVec); - ctrlSigBuf.enq(shiftInputRes.ctrlSig); + let readCrcTabRes = ReadCrcTabRes { + tempCrcVec: tempCrcVec, + ctrlSig: shiftInputRes.ctrlSig + }; + readCrcTabResBuf.enq(readCrcTabRes); endrule - rule reduceTempCrc; - let tempCrcVec = readTabBuf.first; - readTabBuf.deq; - let ctrlSig = ctrlSigBuf.first; - ctrlSigBuf.deq; + rule reduceCrc; + let readCrcTabRes = readCrcTabResBuf.first; + readCrcTabResBuf.deq; - let crcRes = reduceBalancedTree(addCrc, tempCrcVec); - ReduceCrcRes#(dataByteNum, crcWidth) reduceCrcRes = ReduceCrcRes { + let crcRes = reduceBalancedTree(addCrc, readCrcTabRes.tempCrcVec); + ReduceCrcRes#(axiKeepWidth, crcWidth) reduceCrcRes = ReduceCrcRes { crcRes: crcRes, - ctrlSig: ctrlSig + ctrlSig: readCrcTabRes.ctrlSig }; - reduceCrcBuf.enq(reduceCrcRes); + reduceCrcResBuf.enq(reduceCrcRes); + //$display("reduce temp Crc: %x", crcRes); endrule - rule accumulateCrc; - let reduceCrcRes = reduceCrcBuf.first; - reduceCrcBuf.deq; + rule accuCrc; + let reduceCrcRes = reduceCrcResBuf.first; + reduceCrcResBuf.deq; Vector#(crcByteNum, Byte) interCrcVec = unpack(interCrcRes); Vector#(crcByteNum, CrcResult#(crcWidth)) interTempCrcVec = newVector; - for (Integer i = 0; i < valueOf(crcByteNum); i = i + 1) begin - interTempCrcVec[i] = interCrcTabVec[i].sub(interCrcVec[i]); + Integer tabOffset = valueOf(axiKeepWidth); + if (conf.crcMode == CRC_MODE_RECV) begin + Integer initTabOffset = tabOffset - valueOf(crcByteNum); + for (Integer i = 0; i < valueOf(crcByteNum); i = i + 1) begin + if (reduceCrcRes.ctrlSig.isFirst) begin + interTempCrcVec[i] = crcTabVec[i + initTabOffset].sub(interCrcVec[i]); + end + else begin + interTempCrcVec[i] = crcTabVec[i + tabOffset].sub(interCrcVec[i]); + end + end + end + else begin + for (Integer i = 0; i < valueOf(crcByteNum); i = i + 1) begin + interTempCrcVec[i] = crcTabVec[i + tabOffset].sub(interCrcVec[i]); + end end let nextInterCrc = reduceBalancedTree(addCrc, interTempCrcVec); nextInterCrc = nextInterCrc ^ reduceCrcRes.crcRes; - AccumulateRes#(dataByteNum, crcWidth) accuCrcRes = AccumulateRes { + AccuCrcRes#(axiKeepWidth, crcWidth) accuCrcRes = AccuCrcRes { curCrc : reduceCrcRes.crcRes, interCrc: interCrcRes, ctrlSig : reduceCrcRes.ctrlSig }; - if (reduceCrcRes.ctrlSig.tLast) begin - accuCrcBuf.enq(accuCrcRes); + if (reduceCrcRes.ctrlSig.isLast) begin + accuCrcResBuf.enq(accuCrcRes); interCrcRes <= conf.initVal; + //$display("Accumulate Res:", fshow(accuCrcRes)); end else begin interCrcRes <= nextInterCrc; @@ -254,68 +194,104 @@ module mkCrcAxiStream#(CrcConfig#(crcWidth) conf)(CrcAxiStream#(crcWidth, dataBy endrule rule shiftInterCrc; - let accuCrcRes = accuCrcBuf.first; - accuCrcBuf.deq; + let accuCrcRes = accuCrcResBuf.first; + accuCrcResBuf.deq; - Bit#(TAdd#(dataWidth, crcWidth)) interCrc = {accuCrcRes.interCrc, 0}; - interCrc = byteRightShift(interCrc, accuCrcRes.ctrlSig.shiftAmt); + Bit#(TAdd#(axiDataWidth, crcWidth)) interCrc = {accuCrcRes.interCrc, 0}; + Bit#(TAdd#(TLog#(TAdd#(1, axiKeepWidth)), 1)) shiftAmt = zeroExtend(accuCrcRes.ctrlSig.shiftAmt); + if (conf.crcMode == CRC_MODE_RECV) begin + if (accuCrcRes.ctrlSig.isFirst && accuCrcRes.ctrlSig.isLast) begin + shiftAmt = shiftAmt + fromInteger(valueOf(crcByteNum)); + end + end + interCrc = byteRightShift(interCrc, shiftAmt); let shiftInterCrcRes = ShiftInterCrcRes { curCrc: accuCrcRes.curCrc, - remainder: truncate(interCrc), - interCrc: truncateLSB(interCrc) + interCrc: interCrc }; - shiftInterBuf.enq(shiftInterCrcRes); + shiftInterCrcResBuf.enq(shiftInterCrcRes); + //$display("shiftInterCrcRes: %x", interCrc); endrule rule readInterCrcTab; - let shiftInterCrcRes = shiftInterBuf.first; - shiftInterBuf.deq; - Vector#(dataByteNum, Byte) interCrcVec = unpack(shiftInterCrcRes.interCrc); - Vector#(dataByteNum, CrcResult#(crcWidth)) tempCrcVec = newVector; - for (Integer i = 0; i < valueOf(dataByteNum); i = i + 1) begin - tempCrcVec[i] = crcTabVec[i].sub(interCrcVec[i]); + let shiftInterCrcRes = shiftInterCrcResBuf.first; + shiftInterCrcResBuf.deq; + Vector#(interByteNum, Byte) interCrcVec = unpack(shiftInterCrcRes.interCrc); + Vector#(interByteNum, CrcResult#(crcWidth)) readCrcTabResVec = newVector; + for (Integer i = 0; i < valueOf(interByteNum); i = i + 1) begin + readCrcTabResVec[i] = crcTabVec[i].sub(interCrcVec[i]); + //$display("ReadInterCrcTab%d: %x", i, readCrcTabResVec[i]); end - interReadTabBuf.enq(tempCrcVec); - currentCrcBuf.enq(shiftInterCrcRes.curCrc ^ shiftInterCrcRes.remainder); + let readInterCrcTabRes = ReadInterCrcTabRes { + interCrc: readCrcTabResVec, + curCrc: shiftInterCrcRes.curCrc + }; + readInterCrcTabResBuf.enq(readInterCrcTabRes); endrule rule reduceFinalCrc; - let tempCrcVec = interReadTabBuf.first; - interReadTabBuf.deq; - let curCrc = currentCrcBuf.first; - currentCrcBuf.deq; - let interCrc = reduceBalancedTree(addCrc, tempCrcVec); - let finalCrc = interCrc ^ curCrc; - if (conf.reflectRemainder) begin + let readInterCrcTabRes = readInterCrcTabResBuf.first; + readInterCrcTabResBuf.deq; + let interCrc = reduceBalancedTree(addCrc, readInterCrcTabRes.interCrc); + let finalCrc = interCrc ^ readInterCrcTabRes.curCrc; + //$display("final CRC: %x", finalCrc); + if (conf.revOutput == BIT_ORDER_REVERSE) begin finalCrc = reverseBits(finalCrc); end finalCrc = finalCrc ^ conf.finalXor; - finalCrcBuf.enq(finalCrc); + finalCrcResBuf.enq(finalCrc); endrule - interface Put crcReq; - method Action put(AxiStream#(dataByteNum, dataWidth) stream); - // swap endian - stream.tData = swapEndian(stream.tData); - stream.tKeep = reverseBits(stream.tKeep); - // reverse bits of Byte - if (conf.reflectData) begin - stream.tData = reverseBitsOfEachByte(stream.tData); - end - // TODO: modify count Zero logic - let extraByteNum = countZerosLSB(stream.tKeep); - - CrcCtrlSig#(dataByteNum) ctrlSig = CrcCtrlSig { - tLast: stream.tLast, - shiftAmt: pack(extraByteNum) - }; - PreProcessRes#(dataByteNum, dataWidth) preProcessRes = PreProcessRes { - data: stream.tData, - ctrlSig: ctrlSig - }; - preProcessBuf.enq(preProcessRes); - endmethod - endinterface - - interface Get crcResp = toGet(finalCrcBuf); + return convertFifoToPipeOut(finalCrcResBuf); +endmodule + + +interface CrcAxiStream#(numeric type crcWidth, numeric type axiKeepWidth); + interface AxiStreamPut#(axiKeepWidth) crcReq; + interface CrcResultGet#(crcWidth) crcResp; +endinterface + + +module mkCrcAxiStream#( + CrcConfig#(crcWidth) conf +)(CrcAxiStream#(crcWidth, axiKeepWidth)) provisos( + Mul#(BYTE_WIDTH, axiKeepWidth, axiDataWidth), + Mul#(BYTE_WIDTH, crcByteNum, crcWidth), + Mul#(BYTE_WIDTH, interByteNum, TAdd#(axiDataWidth, crcWidth)), + ReduceBalancedTree#(axiKeepWidth, CrcResult#(crcWidth)), + ReduceBalancedTree#(crcByteNum, CrcResult#(crcWidth)), + ReduceBalancedTree#(interByteNum, CrcResult#(crcWidth)) +); + FIFOF#(AxiStream#(axiKeepWidth, AXIS_USER_WIDTH)) crcReqBuf <- mkFIFOF; + let crcReqPipeOut = convertFifoToPipeOut(crcReqBuf); + let crcRespPipeOut <- mkCrcAxiStreamPipeOut(conf, crcReqPipeOut); + + interface Put crcReq = toPut(crcReqBuf); + interface Get crcResp = toGet(crcRespPipeOut); endmodule + + +interface CrcRawAxiStream#(numeric type crcWidth, numeric type axiKeepWidth); + (* prefix = "s_axis" *) + interface RawAxiStreamSlave#(axiKeepWidth, AXIS_USER_WIDTH) rawCrcReq; + (* prefix = "m_crc_stream" *) + interface RawBusMaster#(CrcResult#(crcWidth)) rawCrcResp; +endinterface + +module mkCrcRawAxiStream#(CrcConfig#(crcWidth) conf)(CrcRawAxiStream#(crcWidth, axiKeepWidth)) provisos( + Mul#(BYTE_WIDTH, axiKeepWidth, dataWidth), + Mul#(BYTE_WIDTH, crcByteNum, crcWidth), + Mul#(BYTE_WIDTH, interByteNum, TAdd#(dataWidth, crcWidth)), + ReduceBalancedTree#(axiKeepWidth, CrcResult#(crcWidth)), + ReduceBalancedTree#(crcByteNum, CrcResult#(crcWidth)), + ReduceBalancedTree#(interByteNum, CrcResult#(crcWidth)) +); + + CrcAxiStream#(crcWidth, axiKeepWidth) crcAxiStream <- mkCrcAxiStream(conf); + let rawAxiStreamSlave <- mkPutToRawAxiStreamSlave(crcAxiStream.crcReq, CF); + let rawBusMaster <- mkGetToRawBusMaster(crcAxiStream.crcResp, CF); + + interface RawAxiStreamRecv rawCrcReq = rawAxiStreamSlave; + interface RawBusSend rawCrcResp = rawBusMaster; +endmodule + diff --git a/src/CrcAxiStreamCustom.bsv b/src/CrcAxiStreamCustom.bsv new file mode 100644 index 0000000..5aedf67 --- /dev/null +++ b/src/CrcAxiStreamCustom.bsv @@ -0,0 +1,38 @@ +import CrcDefines :: *; +import CrcAxiStream :: *; + +typedef CrcAxiStream#(`CRC_WIDTH, `AXI_KEEP_WIDTH) CrcAxiStreamCustom; +(* synthesize *) +module mkCrcAxiStreamCustom(CrcAxiStreamCustom); + CrcConfig#(`CRC_WIDTH) conf = CrcConfig { + polynominal: `POLY, + initVal: `INIT_VAL, + finalXor: `FINAL_XOR, + revInput: `REV_INPUT, + revOutput: `REV_OUTPUT, + memFilePrefix: `MEM_FILE_PREFIX, + crcMode: `CRC_MODE + }; + + CrcAxiStreamCustom crc <- mkCrcAxiStream(conf); + return crc; +endmodule + + +typedef CrcRawAxiStream#(`CRC_WIDTH, `AXI_KEEP_WIDTH) CrcRawAxiStreamCustom; +(* synthesize *) +module mkCrcRawAxiStreamCustom(CrcRawAxiStreamCustom); + CrcConfig#(`CRC_WIDTH) conf = CrcConfig { + polynominal: `POLY, + initVal: `INIT_VAL, + finalXor: `FINAL_XOR, + revInput: `REV_INPUT, + revOutput: `REV_OUTPUT, + memFilePrefix: `MEM_FILE_PREFIX, + crcMode: `CRC_MODE + }; + + CrcRawAxiStreamCustom crc <- mkCrcRawAxiStream(conf); + return crc; +endmodule + diff --git a/src/CrcDefines.bsv b/src/CrcDefines.bsv new file mode 100644 index 0000000..a72699d --- /dev/null +++ b/src/CrcDefines.bsv @@ -0,0 +1,64 @@ +import GetPut :: *; + +import SemiFifo :: *; +import AxiStreamTypes :: *; + +//////////////////////////////////////////////////////////////////////////////// +////////// Definition of some common types +//////////////////////////////////////////////////////////////////////////////// +typedef 8 BYTE_WIDTH; +typedef 16 WORD_WIDTH; +typedef Bit#(BYTE_WIDTH) Byte; +typedef Bit#(WORD_WIDTH) Word; + +typedef Bit#(width) CrcResult#(numeric type width); + +// Configuration of CRC hardware implementation +typedef enum { + CRC_MODE_RECV, + CRC_MODE_SEND +} CrcMode deriving(Eq, FShow); + +typedef enum { + BIT_ORDER_REVERSE, + BIT_ORDER_NOT_REVERSE +} IsReverseBitOrder deriving(Eq, FShow); + +typedef struct { + Bit#(crcWidth) polynominal; + Bit#(crcWidth) initVal; + Bit#(crcWidth) finalXor; + IsReverseBitOrder revInput; + IsReverseBitOrder revOutput; + String memFilePrefix; + CrcMode crcMode; +} CrcConfig#(numeric type crcWidth) deriving(Eq, FShow); + +typedef PipeOut#(CrcResult#(crcWidth)) CrcResultPipeOut#(numeric type crcWidth); +typedef PipeOut#(AxiStream#(keepWidth, AXIS_USER_WIDTH)) AxiStreamPipeOut#(numeric type keepWidth); +typedef Get#(CrcResult#(crcWidth)) CrcResultGet#(numeric type crcWidth); +typedef Put#(AxiStream#(keepWidth, AXIS_USER_WIDTH)) AxiStreamPut#(numeric type keepWidth); + + +typedef 8 CRC8_WIDTH; +typedef 16 CRC16_WIDTH; +typedef 32 CRC32_WIDTH; + +typedef 1 AXIS_USER_WIDTH; + +typedef 8 AXIS64_KEEP_WIDTH; +typedef 16 AXIS128_KEEP_WIDTH; +typedef 32 AXIS256_KEEP_WIDTH; +typedef 64 AXIS512_KEEP_WIDTH; + +typedef 8'h07 CRC8_CCITT_POLY; +typedef 16'h8005 CRC16_ANSI_POLY; +typedef 32'h04C11DB7 CRC32_IEEE_POLY; + +typedef 8'h00 CRC8_CCITT_INIT_VAL; +typedef 16'h0000 CRC16_ANSI_INIT_VAL; +typedef 32'hFFFFFFFF CRC32_IEEE_INIT_VAL; + +typedef 8'h00 CRC8_CCITT_FINAL_XOR; +typedef 16'h0000 CRC16_ANSI_FINAL_XOR; +typedef 32'hFFFFFFFF CRC32_IEEE_FINAL_XOR; diff --git a/src/CrcRawAxiStream.bsv b/src/CrcRawAxiStream.bsv deleted file mode 100644 index 7d13790..0000000 --- a/src/CrcRawAxiStream.bsv +++ /dev/null @@ -1,153 +0,0 @@ -import GetPut :: *; -import FIFOF :: *; -import Connectable :: *; - -import CrcAxiStream :: *; - -(* always_ready, always_enabled *) -interface RawBusSend#(type dType); - (* result = "data" *) method dType data; - (* result = "valid" *) method Bool valid; - (* prefix = "" *) method Action ready((* port = "ready" *) Bool rdy); -endinterface - -(* always_ready, always_enabled *) -interface RawBusRecv#(type dType); - (* prefix = "" *) method Action validData( - (* port = "valid" *) Bool valid, - (* port = "data" *) dType data - ); - (* result = "ready" *)method Bool ready; -endinterface - -interface RawBusSender#(type dType); - interface Put#(dType) in; - interface RawBusSend#(dType) out; -endinterface - -interface RawBusReceiver#(type dType); - interface RawBusRecv#(dType) in; - interface Get#(dType) out; -endinterface - -(* always_ready, always_enabled *) -interface RawAxiStreamSend#(numeric type keepWidth, numeric type dataWidth); - (* result = "axis_tvalid" *) method Bool tValid; - (* result = "axis_tdata" *) method Bit#(dataWidth) tData; - (* result = "axis_tkeep" *) method Bit#(keepWidth) tKeep; - (* result = "axis_tlast" *) method Bool tLast; - (* result = "axis_tuser" *) method Bool tUser; - (* prefix = "" *) method Action tReady((* port="axis_tready" *) Bool ready); -endinterface - -(* always_ready, always_enabled *) -interface RawAxiStreamRecv#(numeric type keepWidth, numeric type dataWidth); - (* prefix = "" *) - method Action tValid ( - (* port="axis_tvalid" *) Bool valid, - (* port="axis_tdata" *) Bit#(dataWidth) tData, - (* port="axis_tkeep" *) Bit#(keepWidth) tKeep, - (* port="axis_tlast" *) Bool tLast, - (* port="axis_tuser" *) Bool tUser - ); - (* result="axis_tready" *) - method Bool tReady; -endinterface - - -module mkRawBusSender(RawBusSender#(dType)) provisos(Bits#(dType, dTypeSz)); - Bool unguarded = True; - Bool guarded = False; - FIFOF#(dType) buffer <- mkGFIFOF(guarded, unguarded); - - interface Put in = toPut(buffer); - - interface RawBusSend out; - method Bool valid = buffer.notEmpty; - method dType data = buffer.first; - method Action ready(Bool rdy); - if (rdy && buffer.notEmpty) begin - buffer.deq; - end - endmethod - endinterface -endmodule - -module mkRawBusReceiver(RawBusReceiver#(dType)) provisos(Bits#(dType, dTypeSz)); - Bool unguarded = True; - Bool guarded = False; - FIFOF#(dType) buffer <- mkGFIFOF(unguarded, guarded); - - interface RawBusRecv in; - method Action validData(Bool valid, dType data); - if (valid && buffer.notFull) begin - buffer.enq(data); - end - endmethod - - method Bool ready = buffer.notFull; - endinterface - - interface Get out = toGet(buffer); -endmodule - -module mkRawBusSendToRawAxiStream#( - RawBusSend#(AxiStream#(keepWidth, dataWidth)) rawBusSend - )(RawAxiStreamSend#(keepWidth, dataWidth)); - - method Bool tValid = rawBusSend.valid; - method Bit#(dataWidth) tData = rawBusSend.data.tData; - method Bit#(keepWidth) tKeep = rawBusSend.data.tKeep; - method Bool tLast = rawBusSend.data.tLast; - method Bool tUser = rawBusSend.data.tUser; - method Action tReady(Bool val); - rawBusSend.ready(val); - endmethod -endmodule - -module mkRawBusRecvToRawAxiStream#( - RawBusRecv#(AxiStream#(keepWidth, dataWidth)) rawBusRecv - )(RawAxiStreamRecv#(keepWidth, dataWidth) rawAxiRecv); - - method Action tValid (Bool valid, Bit#(dataWidth) tData, Bit#(keepWidth) tKeep, Bool tLast, Bool tUser); - AxiStream#(keepWidth, dataWidth) axiStream = AxiStream { - tData: tData, - tKeep: tKeep, - tLast: tLast, - tUser: tUser - }; - rawBusRecv.validData(valid, axiStream); - endmethod - - method Bool tReady = rawBusRecv.ready; -endmodule - - -interface CrcRawAxiStream#(numeric type crcWidth, numeric type keepWidth, numeric type dataWidth); - (* prefix = "s" *) - interface RawAxiStreamRecv#(keepWidth, dataWidth) crcReq; // crcReq - (* prefix = "m_crc_stream" *) - interface RawBusSend#(CrcResult#(crcWidth)) crcResp; // crcResp -endinterface - -module mkCrcRawAxiStream#(CrcConfig#(crcWidth) conf)(CrcRawAxiStream#(crcWidth, keepWidth, dataWidth)) - provisos( - Mul#(BYTE_WIDTH, keepWidth, dataWidth), - Mul#(BYTE_WIDTH, crcByteNum, crcWidth), - Mul#(BYTE_WIDTH, interByteNum, TAdd#(dataWidth, crcWidth)), - ReduceBalancedTree#(keepWidth, CrcResult#(crcWidth)), - ReduceBalancedTree#(crcByteNum, CrcResult#(crcWidth)) - ); - - RawBusReceiver#(AxiStream#(keepWidth, dataWidth)) rawBusReceiver <- mkRawBusReceiver; - RawBusSender#(CrcResult#(crcWidth)) rawBusSender <- mkRawBusSender; - CrcAxiStream#(crcWidth, keepWidth, dataWidth) crcAxiStream <- mkCrcAxiStream(conf); - - mkConnection(rawBusReceiver.out, crcAxiStream.crcReq); - mkConnection(crcAxiStream.crcResp, rawBusSender.in); - RawAxiStreamRecv#(keepWidth, dataWidth) rawAxiRecv <- mkRawBusRecvToRawAxiStream(rawBusReceiver.in); - - interface RawAxiStreamRecv crcReq = rawAxiRecv; - interface RawBusSend crcResp = rawBusSender.out; -endmodule - diff --git a/src/CrcRawAxiStreamCustom.bsv b/src/CrcRawAxiStreamCustom.bsv deleted file mode 100644 index db3e8b5..0000000 --- a/src/CrcRawAxiStreamCustom.bsv +++ /dev/null @@ -1,18 +0,0 @@ -import CrcAxiStream :: *; -import CrcRawAxiStream :: *; - -typedef CrcRawAxiStream#(`CRC_WIDTH, `KEEP_WIDTH, `DATA_WIDTH) CrcRawAxiStreamCustom; -(* synthesize *) -module mkCrcRawAxiStreamCustom(CrcRawAxiStreamCustom); - CrcConfig#(`CRC_WIDTH) conf = CrcConfig { - polynominal: `POLY, - initVal : `INIT_VAL, - finalXor : `FINAL_XOR, - reflectData: `REFLECT_IN, - reflectRemainder: `REFLECT_OUT - }; - - CrcRawAxiStreamCustom crc <- mkCrcRawAxiStream(conf); - return crc; -endmodule - diff --git a/src/CrcUtils.bsv b/src/CrcUtils.bsv new file mode 100644 index 0000000..0480aa5 --- /dev/null +++ b/src/CrcUtils.bsv @@ -0,0 +1,85 @@ +import Vector :: *; +import Printf :: *; +import RegFile :: *; + +import CrcDefines :: *; + + +//////////////////////////////////////////////////////////////////////////////// +////////// Implementation of utility functions used in the design +//////////////////////////////////////////////////////////////////////////////// +module mkCrcRegFileTable#(Integer offset, String filePrefix)(Integer idx, RegFile#(Byte, CrcResult#(width)) ifc); + // $display("crc lookup tab offset: %d", offset); + let initFile = sprintf("%s_%d.mem", filePrefix, offset + idx); + RegFile#(Byte, CrcResult#(width)) regFile <- mkRegFileFullLoad(initFile); + return regFile; +endmodule + +function CrcResult#(width) addCrc(CrcResult#(width) crc1, CrcResult#(width) crc2); + return crc1 ^ crc2; +endfunction + +function Bit#(width) swapEndian(Bit#(width) data) provisos(Mul#(BYTE_WIDTH, byteNum, width)); + Vector#(byteNum, Byte) dataVec = unpack(data); + return pack(reverse(dataVec)); +endfunction + +function Bit#(width) reverseBitsOfEachByte(Bit#(width) data) provisos(Mul#(BYTE_WIDTH, byteNum, width)); + Vector#(byteNum, Byte) dataVec = unpack(data); + Vector#(byteNum, Byte) revDataVec = map(reverseBits, dataVec); + return pack(revDataVec); +endfunction + +function Bit#(width) byteRightShift(Bit#(width) dataIn, Bit#(shiftAmtWidth) shiftAmt) + provisos(Mul#(BYTE_WIDTH, byteNum, width)); + Vector#(byteNum, Byte) dataInVec = unpack(dataIn); + dataInVec = shiftOutFrom0(0, dataInVec, shiftAmt); + return pack(dataInVec); +endfunction + +function Bit#(width) byteLeftShift(Bit#(width) dataIn, Bit#(shiftAmtWidth) shiftAmt) + provisos(Mul#(BYTE_WIDTH, byteNum, width)); + + Vector#(byteNum, Byte) dataInVec = unpack(dataIn); + dataInVec = shiftOutFromN(0, dataInVec, shiftAmt); + return pack(dataInVec); +endfunction + +function Bit#(dWidth) bitMask(Bit#(dWidth) data, Bit#(mWidth) mask) provisos(Mul#(mWidth, BYTE_WIDTH, dWidth)); + Bit#(dWidth) fullMask = 0; + for (Integer i = 0; i < valueOf(mWidth); i = i + 1) begin + for (Integer j = 0; j < 8; j = j + 1) begin + fullMask[i*8+j] = mask[i]; + end + end + return fullMask & data; +endfunction + +function Bit#(w) setAllBits; + Bit#(TAdd#(w,1)) result = 1; + return truncate((result << valueOf(w)) - 1); +endfunction + +typeclass ReduceBalancedTree#(numeric type num, type dType); + function dType reduceBalancedTree(function dType op(dType a, dType b), Vector#(num, dType) vecIn); +endtypeclass + +instance ReduceBalancedTree#(2, dType); + function reduceBalancedTree(op, vecIn) = op(vecIn[0], vecIn[1]); +endinstance + +instance ReduceBalancedTree#(1, dType); + function reduceBalancedTree(op, vecIn) = vecIn[0]; +endinstance + +instance ReduceBalancedTree#(num, dType) + provisos(Div#(num, 2, firstHalf), Add#(firstHalf, secondHalf, num), + ReduceBalancedTree#(firstHalf, dType), ReduceBalancedTree#(secondHalf, dType)); + function reduceBalancedTree(op, vecIn); + Vector#(firstHalf, dType) firstHalfVec = take(vecIn); + Vector#(secondHalf, dType) secondHalfVec = drop(vecIn); + let firstHalfRes = reduceBalancedTree(op, firstHalfVec); + let secondHalfRes = reduceBalancedTree(op, secondHalfVec); + return op(firstHalfRes, secondHalfRes); + endfunction +endinstance diff --git a/src/gen_crc_tab.py b/src/gen_crc_tab.py deleted file mode 100644 index bfe249d..0000000 --- a/src/gen_crc_tab.py +++ /dev/null @@ -1,147 +0,0 @@ -import sys -import os - -BYTE_WIDTH = 8 -BYTE_MAX_VAL = pow(2, BYTE_WIDTH) - 1 - - -def bits_reverse(val: int, width: int): - result = 0 - for i in range(width): - result <<= 1 - result |= val & 1 - val >>= 1 - return result - - -def bits_left_shift(val: int, width: int, amt: int): - mask = (1 << width) - 1 - return (val << amt) & mask - - -def bits_msb(val: int, width: int): - return val >> (width - 1) - - -class CrcLookUpTable: - def __init__( - self, - polynominal: bytes, - init_val: bytes, - final_xor: bytes, - rev_input: bool, - rev_output: bool, - ): - self.byte_order = "big" - self.polynominal = polynominal - self.init_val = init_val - self.final_xor = final_xor - self.rev_input = rev_input - self.rev_output = rev_output - self.byte_num = len(polynominal) - self.bit_width = self.byte_num * BYTE_WIDTH - assert len(init_val) == len( - polynominal - ), "The width of initial value doesn't match that of polynomial" - assert len(final_xor) == len( - polynominal - ), "The width of final xor doesn't match that of polynominal" - - def add_byte_to_crc(self, byte_int: int, crc: bytes): - assert byte_int <= BYTE_MAX_VAL, "The value of byte_int argument is illegal" - - crc_int = int.from_bytes(crc, self.byte_order) - polynominal = int.from_bytes(self.polynominal, self.byte_order) - - if self.rev_input: - byte_int = bits_reverse(byte_int, BYTE_WIDTH) - - byte_int = byte_int << (self.bit_width - BYTE_WIDTH) - crc_int = crc_int ^ byte_int - - for i in range(BYTE_WIDTH): - if bits_msb(crc_int, self.bit_width): - crc_int = bits_left_shift(crc_int, self.bit_width, 1) - crc_int = crc_int ^ polynominal - else: - crc_int = bits_left_shift(crc_int, self.bit_width, 1) - - return crc_int.to_bytes(self.byte_num, self.byte_order) - - def crc_output(self, crc: bytes): - crc_int = int.from_bytes(crc, self.byte_order) - final_xor_int = int.from_bytes(self.final_xor, self.byte_order) - - if self.rev_output: - crc_int = bits_reverse(crc_int, self.bit_width) - crc_int = crc_int ^ final_xor_int - - return crc_int.to_bytes(self.byte_num, self.byte_order) - - def gen_byte_crc_tab(self, offset: int): - crc_table = [] - - for i in range(BYTE_MAX_VAL + 1): - crc = self.add_byte_to_crc(i, self.init_val) - for j in range(offset): - crc = self.add_byte_to_crc(0, crc) - - crc = self.crc_output(crc) - crc_table.append(crc) - - return crc_table - - def gen_crc_tab_file(self, file_prefix: str, offset_range: range): - for i in offset_range: - file_name = file_prefix + f"_{i}.mem" - file = open(file_name, "w") - crc_table = self.gen_byte_crc_tab(i) - for crc in crc_table: - if self.byte_order == "little": - crc = crc[::-1] - file.write(crc.hex() + "\n") - file.close() - - -if __name__ == "__main__": - crc8_tab = CrcLookUpTable( - polynominal=b"\x07", - init_val=b"\x00", - final_xor=b"\x00", - rev_input=False, - rev_output=False, - ) - crc16_tab = CrcLookUpTable( - polynominal=b"\x80\x05", - init_val=b"\x00\x00", - final_xor=b"\x00\x00", - rev_input=False, - rev_output=False, - ) - crc32_tab = CrcLookUpTable( - polynominal=b"\x04\xC1\x1D\xB7", - init_val=b"\x00\x00\x00\x00", - final_xor=b"\x00\x00\x00\x00", - rev_input=False, - rev_output=False, - ) - - crc_width_opt = (8, 16, 32) - crc_tab_map = {8: crc8_tab, 16: crc16_tab, 32: crc32_tab} - standard_opt = () # TODO: - - assert len(sys.argv) >= 3, "The number of input arguments is incorrect." - arg = sys.argv - crc_width = int(arg[1]) - assert ( - crc_width in crc_width_opt - ), f"Table generation of {crc_width}-bit hasn't been supported." - input_width = int(arg[2]) - assert input_width % 8 == 0, f"The width of input data must be multiples of 8 bits." - path = "." - if len(sys.argv) > 3: - path = arg[3] - - input_byte_num = int(input_width / 8) - file_prefix = os.path.join(path, "crc_tab") - crc_tab_map[crc_width].gen_crc_tab_file(file_prefix, range(input_byte_num)) diff --git a/test/bluesim/Makefile b/test/bluesim/Makefile index ebd6326..c6a37f2 100644 --- a/test/bluesim/Makefile +++ b/test/bluesim/Makefile @@ -1,22 +1,50 @@ -include ../../scripts/Makefile.base - -CRC_WIDTH = 8 -AXI_WIDTH = 512 -FILE ?= TestCrc$(CRC_WIDTH)AxiStream.bsv -TOP ?= mkTestCrc$(CRC_WIDTH)AxiStream$(AXI_WIDTH) +ROOT_DIR = $(abspath ../../) +SCRIPTS_DIR = $(ROOT_DIR)/scripts +include $(SCRIPTS_DIR)/Makefile.base +LOCALSRCDIR = $(ROOT_DIR)/src +LIBSRCDIR = $(ROOT_DIR)/lib/blue-wrapper/src + +# CRC Configurations +JSON_CONF_FILE = $(SCRIPTS_DIR)/config/crc_32_256_recv.json +CRC_WIDTH = 32 +AXI_KEEP_WIDTH = 32 +POLY = 79764919 +INIT_VAL = 4294967295 +FINAL_XOR = 4294967295 +REV_INPUT = BIT_ORDER_REVERSE +REV_OUTPUT = BIT_ORDER_REVERSE +MEM_FILE_PREFIX = crc_tab +CRC_MODE = CRC_MODE_RECV + +MACROFLAGS = -D AXI_KEEP_WIDTH=$(CRC_WIDTH) \ + -D CRC_WIDTH=$(AXI_KEEP_WIDTH) \ + -D POLY=$(POLY) \ + -D INIT_VAL=$(INIT_VAL) \ + -D FINAL_XOR=$(FINAL_XOR) \ + -D REV_INPUT=$(REV_INPUT) \ + -D REV_OUTPUT=$(REV_OUTPUT) \ + -D MEM_FILE_PREFIX="\"$(MEM_FILE_PREFIX)\"" \ + -D CRC_MODE=$(CRC_MODE) + +FILE ?= TestCrcAxiStream.bsv +TOP ?= mkTestCrcAxiStream SIMEXE = $(BUILDDIR)/$(TOP).exe +CRC_TAB_SCRIPTS = $(SCRIPTS_DIR)/gen_crc_tab.py +TABDIR = . + +table: + python3 $(CRC_TAB_SCRIPTS) $(JSON_CONF_FILE) $(TABDIR) -compile: table +compile: mkdir -p $(BUILDDIR) # bsc -elab -sim -verbose $(BLUESIMFLAGS) $(DEBUGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) -g $(TOP) $(FILE) - bsc -elab -sim $(BLUESIMFLAGS) $(DEBUGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) -g $(TOP) $(FILE) + bsc -elab -sim $(BLUESIMFLAGS) $(DEBUGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) $(MACROFLAGS) -g $(TOP) $(FILE) link: compile bsc -sim $(BLUESIMFLAGS) $(DIRFLAGS) $(RECOMPILEFLAGS) $(SCHEDFLAGS) $(TRANSFLAGS) -e $(TOP) -o $(SIMEXE) -simulate: link - python3 ../../src/gen_crc_tab.py $(CRC_WIDTH) $(AXI_WIDTH) +simulate: link table $(SIMEXE) clean: diff --git a/test/bluesim/TestCrc16AxiStream.bsv b/test/bluesim/TestCrc16AxiStream.bsv deleted file mode 100644 index 89eca90..0000000 --- a/test/bluesim/TestCrc16AxiStream.bsv +++ /dev/null @@ -1,121 +0,0 @@ -import FIFOF :: *; -import Randomizable :: *; -import CRC :: *; -import Vector :: *; -import GetPut :: *; -import Connectable :: *; - -import CrcAxiStream :: *; -import Crc16AxiStream :: *; -import TestCrcAxiStream :: *; -import TestUtils :: *; - -typedef 16 CYCLE_COUNT_WIDTH; -typedef 50000 MAX_CYCLE; -typedef 32 CASE_NUM; -typedef 8 CASE_COUNT_WIDTH; -typedef 133 CASE_BYTE_WIDTH; - -(* synthesize *) -module mkTestCrc16AxiStream64 (Empty); - - CrcConfig#(CRC16_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC16_WIDTH, - AXIS64_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc16AxiStream128 (Empty); - - CrcConfig#(CRC16_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC16_WIDTH, - AXIS128_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc16AxiStream256 (Empty); - - CrcConfig#(CRC16_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC16_WIDTH, - AXIS256_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc16AxiStream512 (Empty); - - CrcConfig#(CRC16_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC16_ANSI_POLY)), - initVal : fromInteger(valueOf(CRC16_ANSI_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC16_ANSI_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC16_WIDTH, - AXIS512_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule \ No newline at end of file diff --git a/test/bluesim/TestCrc32AxiStream.bsv b/test/bluesim/TestCrc32AxiStream.bsv deleted file mode 100644 index 0b2fa8c..0000000 --- a/test/bluesim/TestCrc32AxiStream.bsv +++ /dev/null @@ -1,122 +0,0 @@ -import FIFOF :: *; -import Randomizable :: *; -import CRC :: *; -import Vector :: *; -import GetPut :: *; -import Connectable :: *; - -import CrcAxiStream :: *; -import Crc32AxiStream :: *; -import TestCrcAxiStream :: *; -import TestUtils :: *; - -typedef 16 CYCLE_COUNT_WIDTH; -typedef 50000 MAX_CYCLE; -typedef 32 CASE_NUM; -typedef 8 CASE_COUNT_WIDTH; -typedef 133 CASE_BYTE_WIDTH; - -(* synthesize *) -module mkTestCrc32AxiStream64 (Empty); - - CrcConfig#(CRC32_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC32_WIDTH, - AXIS64_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc32AxiStream128 (Empty); - - CrcConfig#(CRC32_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC32_WIDTH, - AXIS128_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc32AxiStream256 (Empty); - - CrcConfig#(CRC32_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC32_WIDTH, - AXIS256_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc32AxiStream512 (Empty); - - CrcConfig#(CRC32_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC32_IEEE_POLY)), - initVal : fromInteger(valueOf(CRC32_IEEE_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC32_IEEE_FINAL_XOR)), - reflectData: True, - reflectRemainder: True - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC32_WIDTH, - AXIS512_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - diff --git a/test/bluesim/TestCrc8AxiStream.bsv b/test/bluesim/TestCrc8AxiStream.bsv deleted file mode 100644 index 7de6db8..0000000 --- a/test/bluesim/TestCrc8AxiStream.bsv +++ /dev/null @@ -1,121 +0,0 @@ -import FIFOF :: *; -import Randomizable :: *; -import CRC :: *; -import Vector :: *; -import GetPut :: *; -import Connectable :: *; - -import CrcAxiStream :: *; -import Crc8AxiStream :: *; -import TestCrcAxiStream :: *; -import TestUtils :: *; - -typedef 16 CYCLE_COUNT_WIDTH; -typedef 10000 MAX_CYCLE; -typedef 32 CASE_NUM; -typedef 8 CASE_COUNT_WIDTH; -typedef 133 CASE_BYTE_WIDTH; - -(* synthesize *) -module mkTestCrc8AxiStream64 (Empty); - - CrcConfig#(CRC8_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC8_WIDTH, - AXIS64_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc8AxiStream128 (Empty); - - CrcConfig#(CRC8_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC8_WIDTH, - AXIS128_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc8AxiStream256 (Empty); - - CrcConfig#(CRC8_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC8_WIDTH, - AXIS256_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule - -(* synthesize *) -module mkTestCrc8AxiStream512 (Empty); - - CrcConfig#(CRC8_WIDTH) crcConf = CrcConfig { - polynominal: fromInteger(valueOf(CRC8_CCITT_POLY)), - initVal : fromInteger(valueOf(CRC8_CCITT_INIT_VAL)), - finalXor : fromInteger(valueOf(CRC8_CCITT_FINAL_XOR)), - reflectData: False, - reflectRemainder: False - }; - - TestCrcAxiStreamConfig#( - CYCLE_COUNT_WIDTH, - CASE_COUNT_WIDTH, - CASE_BYTE_WIDTH, - CRC8_WIDTH, - AXIS512_KEEP_WIDTH - ) testConfig = TestCrcAxiStreamConfig { - maxCycle: fromInteger(valueOf(MAX_CYCLE)), - caseNum: fromInteger(valueOf(CASE_NUM)), - crcConfig: crcConf - }; - - mkTestCrcAxiStream(testConfig); -endmodule \ No newline at end of file diff --git a/test/bluesim/TestCrcAxiStream.bsv b/test/bluesim/TestCrcAxiStream.bsv index f456b1d..799e026 100644 --- a/test/bluesim/TestCrcAxiStream.bsv +++ b/test/bluesim/TestCrcAxiStream.bsv @@ -5,70 +5,96 @@ import Vector :: *; import GetPut :: *; import Connectable :: *; +import CrcDefines :: *; +import CrcUtils :: *; import CrcAxiStream :: *; import TestUtils :: *; -// Configuration of CRC Tester -typedef struct { - Bit#(cycleCountWidth) maxCycle; - Bit#(caseCountWidth) caseNum; - CrcConfig#(crcWidth) crcConfig; -} TestCrcAxiStreamConfig#( - numeric type cycleCountWidth, - numeric type caseCountWidth, - numeric type caseByteNum, - numeric type crcWidth, - numeric type axiByteNum -); - -module mkTestCrcAxiStream#( - TestCrcAxiStreamConfig#( - cycleCountWidth, - caseCountWidth, - caseByteNum, - crcWidth, - axiByteNum - ) conf -)(Empty) provisos( - Mul#(crcByteNum, BYTE_WIDTH, crcWidth), - Mul#(caseByteNum, BYTE_WIDTH, caseWidth), - Mul#(axiByteNum, BYTE_WIDTH, axiWidth), - ReduceBalancedTree#(axiByteNum, Bit#(crcWidth)), - ReduceBalancedTree#(crcByteNum, Bit#(crcWidth)), - - Add#(8, a__, caseWidth), - Add#(8, e__, crcWidth), - Mul#(8, b__, TAdd#(axiWidth, crcWidth)), - Add#(caseByteNum, c__, TMul#(axiByteNum, TDiv#(caseWidth, axiWidth))), - Add#(caseWidth, d__, TMul#(TDiv#(caseWidth, axiWidth), axiWidth)) -); - let crcConf = conf.crcConfig; - Reg#(Bit#(caseCountWidth)) inputCaseCount <- mkReg(0); - Reg#(Bit#(caseCountWidth)) outputCaseCount <- mkReg(0); - Reg#(Bit#(TLog#(TAdd#(caseByteNum, 1)))) caseByteCount <- mkReg(0); +import SemiFifo :: *; + +// `define CRC_WIDTH 32 +// `define AXI_KEEP_WIDTH 32 +//`define POLY 32'h04C11DB7 +// `define INIT_VAL 32'hFFFFFFFF +// `define FINAL_XOR 32'hFFFFFFFF +// `define REV_INPUT BIT_ORDER_REVERSE +// `define REV_OUTPUT BIT_ORDER_REVERSE +// `define MEM_FILE_PREFIX "crc_tab" +// `define CRC_MODE CRC_MODE_RECV + +typedef 512 TEST_CASE_NUM; +typedef 16 CYCLE_COUNT_WIDTH; +typedef 40000 MAX_CYCLE_NUM; +typedef 16 CASE_COUNT_WIDTH; + +typedef 2 MAX_FRAG_NUM; +typedef TMul#(MAX_FRAG_NUM, `AXI_KEEP_WIDTH) MAX_RAW_DATA_BYTE_NUM; +typedef TMul#(MAX_RAW_DATA_BYTE_NUM, BYTE_WIDTH) MAX_RAW_DATA_WIDTH; +typedef TLog#(TAdd#(MAX_RAW_DATA_BYTE_NUM, 1)) RAW_DATA_COUNT_WIDTH; + +typedef TDiv#(`CRC_WIDTH, BYTE_WIDTH) CRC_BYTE_NUM; + +module mkTestCrcAxiStream(Empty); + Integer maxCycleNum = valueOf(MAX_CYCLE_NUM); + Integer testCaseNum = valueOf(TEST_CASE_NUM); + Integer maxRawDataByteNum = valueOf(MAX_RAW_DATA_BYTE_NUM); + Integer crcByteNum = valueOf(CRC_BYTE_NUM); - CRC#(crcWidth) refCrcModel <- mkCRC( - crcConf.polynominal, - crcConf.initVal, - crcConf.finalXor, - crcConf.reflectData, - crcConf.reflectRemainder - ); - FIFOF#(CrcResult#(crcWidth)) refOutputBuf <- mkFIFOF; + // Common Signals + Reg#(Bool) isInit <- mkReg(False); + Reg#(Bit#(CYCLE_COUNT_WIDTH)) cycle <- mkReg(0); + Reg#(Bit#(CASE_COUNT_WIDTH)) inputCaseCount <- mkReg(0); + Reg#(Bit#(CASE_COUNT_WIDTH)) outputCaseCount <- mkReg(0); - AxiStreamSender#( - caseWidth, axiByteNum, axiWidth, caseCountWidth - ) axiSender <- mkAxiStreamSender; - CrcAxiStream#(crcWidth, axiByteNum, axiWidth) dutCrcModel <- mkCrcAxiStream(crcConf); + // Random Signals + Randomize#(Bit#(RAW_DATA_COUNT_WIDTH)) randRawDataByteNum <- mkGenericRandomizer; + Randomize#(Bit#(MAX_RAW_DATA_WIDTH)) randRawData <- mkGenericRandomizer; - mkConnection(dutCrcModel.crcReq, axiSender.axiStreamOut); - Reg#(Bool) isInit <- mkReg(False); - Reg#(Bit#(cycleCountWidth)) cycle <- mkReg(0); - Randomize#(Bit#(caseWidth)) caseDataRand <- mkGenericRandomizer; + // DUT and REF Model + Bit#(`CRC_WIDTH) polynominal = `POLY; + Bit#(`CRC_WIDTH) initVal = `INIT_VAL; + Bit#(`CRC_WIDTH) finalXor = `FINAL_XOR; + IsReverseBitOrder revInput = `REV_INPUT; + IsReverseBitOrder revOutput = `REV_OUTPUT; + String memFilePrefix = `MEM_FILE_PREFIX; + CrcMode crcMode = `CRC_MODE; + + CRC#(`CRC_WIDTH) refCrcModel <- mkCRC( + polynominal, + initVal, + finalXor, + revInput == BIT_ORDER_REVERSE, + revOutput == BIT_ORDER_REVERSE + ); + + CrcConfig#(`CRC_WIDTH) crcConf = CrcConfig { + polynominal: `POLY, + initVal: `INIT_VAL, + finalXor: `FINAL_XOR, + revInput: `REV_INPUT, + revOutput: `REV_OUTPUT, + memFilePrefix: `MEM_FILE_PREFIX, + crcMode: `CRC_MODE + }; + + FIFOF#(Bit#(`CRC_WIDTH)) refCrcOutputBuf <- mkFIFOF; + FIFOF#(Bit#(MAX_RAW_DATA_WIDTH)) rawDataBuf <- mkFIFOF; + FIFOF#(Bit#(RAW_DATA_COUNT_WIDTH)) rawDataByteNumBuf <- mkFIFOF; + AxiStreamPipeOut#(`AXI_KEEP_WIDTH) dutAxiStreamInput <- mkAxiStreamSender( + "AxiStreamSender", + convertFifoToPipeOut(rawDataByteNumBuf), + convertFifoToPipeOut(rawDataBuf) + ); + let dutCrcOutput <- mkCrcAxiStreamPipeOut( + crcConf, + dutAxiStreamInput + ); + rule doRandInit if (!isInit); - caseDataRand.cntrl.init; + randRawDataByteNum.cntrl.init; + randRawData.cntrl.init; refCrcModel.clear; isInit <= True; endrule @@ -76,55 +102,77 @@ module mkTestCrcAxiStream#( rule doCycleCount if (isInit); cycle <= cycle + 1; immAssert( - cycle != conf.maxCycle, - "Testbench timeout assertion @ mkTestUdpEthRxTx", - $format("Cycle count can't overflow %d", conf.maxCycle) + cycle <= fromInteger(maxCycleNum), + "Testbench timeout assertion @ mkTestCrcAxiStream", + $format("Cycle count can't overflow %d", maxCycleNum) ); - $display("\nCycle %d -----------------------------------", cycle); + $display("\nCycle %d ----------------------------------------", cycle); endrule - Reg#(Bit#(caseWidth)) tempCaseData <- mkRegU; - rule genTestCase if (isInit && inputCaseCount < conf.caseNum); - if (caseByteCount == 0) begin - let randData <- caseDataRand.next; - tempCaseData <= randData; - refCrcModel.add(truncateLSB(randData)); - caseByteCount <= caseByteCount + 1; - $display("Gen random test case: %x", randData); + Reg#(Bit#(RAW_DATA_COUNT_WIDTH)) rawDataByteCounter <- mkReg(0); + Reg#(Bit#(MAX_RAW_DATA_WIDTH)) tempRawData <- mkRegU; + Reg#(Bit#(RAW_DATA_COUNT_WIDTH)) tempRawDataByteNum <- mkRegU; + rule genDutInput if (isInit && inputCaseCount < fromInteger(testCaseNum)); + if (rawDataByteCounter == 0) begin + let rawData <- randRawData.next; + let rawDataByteNum <- randRawDataByteNum.next; + Bit#(RAW_DATA_COUNT_WIDTH) maxCaseByteNum = fromInteger(maxRawDataByteNum); + + if (crcMode == CRC_MODE_RECV) begin + maxCaseByteNum = maxCaseByteNum - fromInteger(crcByteNum); + end + if (rawDataByteNum > maxCaseByteNum) begin + rawDataByteNum = maxCaseByteNum; + end + tempRawData <= bitMask(rawData, (1 << rawDataByteNum) - 1); + tempRawDataByteNum <= rawDataByteNum; + $display("input case %d raw data byte num %d", inputCaseCount, rawDataByteNum); + if (rawDataByteNum > 0) begin + refCrcModel.add(truncate(rawData)); + rawDataByteCounter <= rawDataByteCounter + 1; + end end - else if (caseByteCount < fromInteger(valueOf(caseByteNum))) begin - Vector#(caseByteNum, Byte) caseDataVec = unpack(tempCaseData); - caseDataVec = reverse(caseDataVec); - refCrcModel.add(caseDataVec[caseByteCount]); - caseByteCount <= caseByteCount + 1; - $display("Add Ref Crc32: %x", caseDataVec[caseByteCount]); + else if (rawDataByteCounter < tempRawDataByteNum) begin + Vector#(MAX_RAW_DATA_BYTE_NUM, Byte) rawDataVec = unpack(tempRawData); + refCrcModel.add(rawDataVec[rawDataByteCounter]); + rawDataByteCounter <= rawDataByteCounter + 1; + $display("Case %d Add Ref Crc32: %x", inputCaseCount, rawDataVec[rawDataByteCounter]); end else begin - caseByteCount <= 0; + rawDataByteCounter <= 0; inputCaseCount <= inputCaseCount + 1; - axiSender.rawDataIn.put(tempCaseData); - let refCheckSum <- refCrcModel.complete; - refOutputBuf.enq(refCheckSum); - $display("Generate %d input case: %x crc: %x", inputCaseCount, tempCaseData, refCheckSum); + let refCrc <- refCrcModel.complete; + + let dataByteNum = tempRawDataByteNum; + if (crcMode == CRC_MODE_RECV) begin + dataByteNum = dataByteNum + fromInteger(crcByteNum); + end + rawDataBuf.enq(tempRawData); + rawDataByteNumBuf.enq(dataByteNum); + + refCrcOutputBuf.enq(refCrc); + $display("Generate %d input case: %x crc: %x", inputCaseCount, tempRawData, refCrc); end endrule - rule checkDutOuput if (isInit && outputCaseCount < conf.caseNum); - let refOutput = refOutputBuf.first; - refOutputBuf.deq; - let dutOutput <- dutCrcModel.crcResp.get; - $display("Revc case %d output: DUT=%x REF=%x", outputCaseCount,dutOutput, refOutput); + rule checkDutOuput if (isInit && outputCaseCount < fromInteger(testCaseNum)); + let dutCrc = dutCrcOutput.first; + dutCrcOutput.deq; + let refCrc = refCrcOutputBuf.first; + refCrcOutputBuf.deq; + + $display("Case %d DUT Output: %x", outputCaseCount, dutCrc); immAssert( - dutOutput == refOutput, - "Check meta data from CrcAxiStream @ mkCrcAxiStream", - $format("The output of dut and ref are inconsistent") + dutCrc == refCrc, + "The output of DUT and REF are inconsistent @ mkTestCrcAxiStream", + $format("DUT: %x REF: %x", dutCrc, refCrc) ); outputCaseCount <= outputCaseCount + 1; endrule - rule doFinish if (outputCaseCount == conf.caseNum); - $display("Pass all %d test cases!", conf.caseNum); + rule doFinish if (outputCaseCount == fromInteger(testCaseNum)); + $display("Pass all %d test cases!", testCaseNum); $display("0"); $finish; endrule -endmodule +endmodule \ No newline at end of file diff --git a/test/bluesim/TestUtils.bsv b/test/bluesim/TestUtils.bsv index 5bf6d88..a70035e 100644 --- a/test/bluesim/TestUtils.bsv +++ b/test/bluesim/TestUtils.bsv @@ -2,7 +2,13 @@ import FIFOF :: *; import GetPut :: *; import Vector :: *; +import CrcUtils :: *; +import CrcDefines :: *; import CrcAxiStream :: *; +import AxiStreamTypes :: *; + +import SemiFifo :: *; + function Action immAssert(Bool condition, String assertName, Fmt assertFmtMsg); action let pos = printPosition(getStringPosition(assertName)); @@ -29,67 +35,117 @@ function Action immFail(String assertName, Fmt assertFmtMsg); endaction endfunction -interface AxiStreamSender#( - numeric type rawWidth, - numeric type axiByteNum, - numeric type axiWidth, - numeric type caseCountWidth +// interface AxiStreamSender#( +// numeric type rawDataWidth, +// numeric type axiKeepWidth, +// numeric type axiUsrWidth, +// numeric type caseCountWidth +// ); +// interface Put#(Bit#(rawDataWidth)) rawDataIn; +// interface Get#(AxiStream#(axiKeepWidth, axiUsrWidth)) axiStreamOut; +// endinterface + +// module mkAxiStreamSender(AxiStreamSender#(rawDataWidth, axiKeepWidth, axiUsrWidth, caseCountWidth)) +// provisos( +// Mul#(axiKeepWidth, BYTE_WIDTH, axiDataWidth), +// Mul#(rawKeepWidth, BYTE_WIDTH, rawDataWidth), +// Div#(rawDataWidth, axiDataWidth, fragNum), +// Add#(rawKeepWidth, extraKeepWidth, TMul#(axiKeepWidth, fragNum)), +// Add#(rawDataWidth, extraDataWidth, TMul#(fragNum, axiDataWidth)) +// ); + +// Reg#(Bit#(caseCountWidth)) caseCounter <- mkReg(0); +// Reg#(Bit#(TLog#(fragNum))) fragCounter <- mkReg(0); +// FIFOF#(Bit#(rawDataWidth)) inputBuf <- mkFIFOF; +// FIFOF#(AxiStream#(axiKeepWidth, axiUsrWidth)) outputBuf <- mkFIFOF; + +// rule send; +// Integer maxFragNum = valueOf(fragNum) - 1; +// let tLast = fragCounter == fromInteger(maxFragNum); +// Bit#(TMul#(fragNum, axiDataWidth)) rawData = {inputBuf.first, 0}; +// Vector#(fragNum, Bit#(axiDataWidth)) rawDataVec = unpack(rawData); +// rawDataVec = reverse(rawDataVec); + +// AxiStream#(axiKeepWidth, axiUsrWidth) fragment = AxiStream { +// tData : swapEndian(rawDataVec[fragCounter]), +// tKeep : maxBound, +// tLast : tLast, +// tUser : 0 +// }; + +// if (tLast) begin +// fragment.tKeep = fragment.tKeep >> valueOf(extraKeepWidth); +// end + +// outputBuf.enq(fragment); +// $display("AxiStreamSender: send %4d fragment of %4d case", fragCounter, caseCounter); +// $display(fshow(fragment)); +// if (tLast) begin +// fragCounter <= 0; +// $display("AxiStreamSender: complete sending %4d case", caseCounter); +// inputBuf.deq; +// end +// else begin +// fragCounter <= fragCounter + 1; +// end +// endrule + +// interface Put rawDataIn; +// method Action put(Bit#(rawDataWidth) data); +// inputBuf.enq(data); +// caseCounter <= caseCounter + 1; +// $display("AxiStreamSender: start sending %5d case", caseCounter); +// $display("AxiStreamSender: Raw data of %5d: %x", caseCounter, data); +// endmethod +// endinterface +// interface Get axiStreamOut = toGet(outputBuf); +// endmodule + +module mkAxiStreamSender#( + String instanceName, + PipeOut#(Bit#(maxRawByteNumWidth)) rawByteNumIn, + PipeOut#(Bit#(maxRawDataWidth)) rawDataIn +)(PipeOut#(AxiStream#(axiKeepWidth, axiUsrWidth))) provisos( + Mul#(axiKeepWidth, BYTE_WIDTH, axiDataWidth), + Mul#(maxFragNum, axiKeepWidth, maxRawByteNum), + Mul#(maxRawByteNum, BYTE_WIDTH, maxRawDataWidth), + NumAlias#(TLog#(TAdd#(maxRawByteNum, 1)), maxRawByteNumWidth), + NumAlias#(TLog#(maxFragNum), maxFragNumWidth) ); - interface Put#(Bit#(rawWidth)) rawDataIn; - interface Get#(AxiStream#(axiByteNum, axiWidth)) axiStreamOut; -endinterface - -module mkAxiStreamSender(AxiStreamSender#(rawWidth, axiByteNum, axiWidth, caseCountWidth)) - provisos( - Mul#(axiByteNum, BYTE_WIDTH, axiWidth), - Mul#(rawByteNum, BYTE_WIDTH, rawWidth), - Div#(rawWidth, axiWidth, fragNum), - Add#(rawByteNum, extraByteNum, TMul#(axiByteNum, fragNum)), - Add#(rawWidth, extraWidth, TMul#(fragNum, axiWidth)) - ); - Reg#(Bit#(caseCountWidth)) caseCounter <- mkReg(0); - Reg#(Bit#(TLog#(fragNum))) fragmentCount <- mkReg(0); - FIFOF#(Bit#(rawWidth)) inputBuf <- mkFIFOF; - FIFOF#(AxiStream#(axiByteNum, axiWidth)) outputBuf <- mkFIFOF; - - rule send; - Integer maxFragNum = valueOf(fragNum) - 1; - let tLast = fragmentCount == fromInteger(maxFragNum); - Bit#(TMul#(fragNum, axiWidth)) rawData = {inputBuf.first, 0}; - Vector#(fragNum, Bit#(axiWidth)) rawDataVec = unpack(rawData); - rawDataVec = reverse(rawDataVec); - - AxiStream#(axiByteNum, axiWidth) fragment = AxiStream { - tData : swapEndian(rawDataVec[fragmentCount]), - tKeep : maxBound, - tLast : tLast, - tUser : False - }; + Reg#(Bit#(maxFragNumWidth)) fragCounter <- mkReg(0); + Reg#(Bit#(maxRawByteNumWidth)) rawDataByteCounter <- mkReg(0); + FIFOF#(AxiStream#(axiKeepWidth, axiUsrWidth)) outputBuf <- mkFIFOF; - if (tLast) begin - fragment.tKeep = fragment.tKeep >> valueOf(extraByteNum); - end + rule doFragment; + let rawData = rawDataIn.first; + Vector#(maxFragNum, Bit#(axiDataWidth)) rawDataVec = unpack(rawData); + let rawByteNum = rawByteNumIn.first; - outputBuf.enq(fragment); - $display("AxiStreamSender: send %5d fragment of %5d case", caseCounter, fragmentCount); - $display(fshow(fragment)); - if (tLast) begin - fragmentCount <= 0; - $display("AxiStreamSender: complete sending %5d case", caseCounter); - inputBuf.deq; + AxiStream#(axiKeepWidth, axiUsrWidth) axiStream = AxiStream { + tData: rawDataVec[fragCounter], + tKeep: setAllBits, + tLast: False, + tUser: 0 + }; + + let nextRawByteCountVal = rawDataByteCounter + fromInteger(valueOf(axiKeepWidth)); + if (nextRawByteCountVal >= rawByteNum) begin + let extraByteNum = nextRawByteCountVal - rawByteNum; + axiStream.tKeep = axiStream.tKeep >> extraByteNum; + axiStream.tLast = True; + fragCounter <= 0; + rawDataByteCounter <= 0; + rawDataIn.deq; + rawByteNumIn.deq; end else begin - fragmentCount <= fragmentCount + 1; + fragCounter <= fragCounter + 1; + rawDataByteCounter <= nextRawByteCountVal; end - endrule - interface Put rawDataIn; - method Action put(Bit#(rawWidth) data); - inputBuf.enq(data); - caseCounter <= caseCounter + 1; - $display("AxiStreamSender: start sending %5d case", caseCounter); - $display("AxiStreamSender: Raw data of %5d: %x", caseCounter, fshow(data)); - endmethod - endinterface - interface Get axiStreamOut = toGet(outputBuf); -endmodule \ No newline at end of file + outputBuf.enq(axiStream); + $display("%s: send %8d fragment %s", instanceName, fragCounter, fshow(axiStream)); + endrule + + return convertFifoToPipeOut(outputBuf); +endmodule diff --git a/test/bluesim/run_tests.sh b/test/bluesim/run_tests.sh deleted file mode 100755 index d13ade3..0000000 --- a/test/bluesim/run_tests.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -log_dir="logs" -crc_width_opt=(8 16 32) -axi_width_opt=(64 128 256 512) -protocol_opt=() -pass_all=0 - -mkdir -p ${log_dir} -for crc_width in ${crc_width_opt[@]}; do - for axi_width in ${axi_width_opt[@]}; do - module="mkTestCrc${crc_width}AxiStream${axi_width}" - log_file="${log_dir}/${module}.log" - echo "Generate Loopup Tables for ${module}" - python3 ../../src/gen_crc_tab.py ${crc_width} ${axi_width} - - echo "Start Testing ${module}" - make CRC_WIDTH=${crc_width} AXI_WIDTH=${axi_width} > ${log_file} - result=$(tail -n 1 ${log_file}) - - if [ ${result} == 0 ] - then - echo "Pass tests of ${module}" - else - echo "Tests of ${module} fail" - pass_all=1 - fi - echo "" - rm -f *.mem - done -done -echo "Complete All tests" -return $pass_all \ No newline at end of file diff --git a/test/cocotb/Makefile b/test/cocotb/Makefile index 43f8faa..26ea4c8 100644 --- a/test/cocotb/Makefile +++ b/test/cocotb/Makefile @@ -1,29 +1,56 @@ -SCRIPTS_PATH = ../../scripts +ROOT_DIR = $(abspath ../../) +SCRIPTS_DIR = $(ROOT_DIR)/scripts +include $(SCRIPTS_DIR)/Makefile.base +LOCALSRCDIR = $(ROOT_DIR)/src +LIBSRCDIR = $(ROOT_DIR)/lib/blue-wrapper/src -include $(SCRIPTS_PATH)/GenVerilog.mk +FILE = CrcAxiStreamCustom.bsv +TOP = mkCrcRawAxiStreamCustom +VLOGDIR = verilog +TABDIR = $(BUILDDIR) +VLOGFILE = $(VLOGDIR)/$(TOP).v +LIST_VERILOG_TCL = $(SCRIPTS_DIR)/listVlogFiles.tcl + +# CRC Configurations +JSON_CONF_FILE = $(SCRIPTS_DIR)/config/crc_32_256_recv.json CRC_WIDTH = 32 -AXI_WIDTH = 256 -FILE = Crc$(CRC_WIDTH)AxiStream.bsv -TOP = mkCrc$(CRC_WIDTH)RawAxiStream$(AXI_WIDTH) -FILE_PATH = ../../src -VLOGDIR = ./generated -TABDIR = . +AXI_KEEP_WIDTH = 32 +POLY = 79764919 +INIT_VAL = 4294967295 +FINAL_XOR = 4294967295 +REV_INPUT = BIT_ORDER_REVERSE +REV_OUTPUT = BIT_ORDER_REVERSE +MEM_FILE_PREFIX = crc_tab +CRC_MODE = CRC_MODE_RECV + +MACROFLAGS = -D CRC_WIDTH=$(CRC_WIDTH) \ + -D AXI_KEEP_WIDTH=$(AXI_KEEP_WIDTH) \ + -D POLY=$(POLY) \ + -D INIT_VAL=$(INIT_VAL) \ + -D FINAL_XOR=$(FINAL_XOR) \ + -D REV_INPUT=$(REV_INPUT) \ + -D REV_OUTPUT=$(REV_OUTPUT) \ + -D MEM_FILE_PREFIX="\"$(MEM_FILE_PREFIX)\"" \ + -D CRC_MODE=$(CRC_MODE) table: - python3 $(FILE_PATH)/gen_crc_tab.py $(CRC_WIDTH) $(AXI_WIDTH) $(TABDIR) + mkdir -p $(TABDIR) + python3 $(SCRIPTS_DIR)/gen_crc_tab.py $(JSON_CONF_FILE) $(TABDIR) -cocotb: verilog table - python3 testCrcAxiStream.py $(CRC_WIDTH) $(AXI_WIDTH) +verilog: table + mkdir -p $(BUILDDIR) + bsc -elab $(VERILOGFLAGS) $(DIRFLAGS) $(MISCFLAGS) $(RECOMPILEFLAGS) $(RUNTIMEFLAGS) $(TRANSFLAGS) $(MACROFLAGS) -g $(TOP) $(LOCALSRCDIR)/$(FILE) + mkdir -p $(VLOGDIR) + echo "" > $(VLOGFILE) + bluetcl $(LIST_VERILOG_TCL) -bdir $(BUILDDIR) -vdir $(BUILDDIR) $(TOP) $(TOP) | grep -i '\.v' | xargs -I {} cat {} >> $(VLOGFILE) + +cocotb: verilog + python3 TestCrcAxiStream.py $(JSON_CONF_FILE) clean: - rm -rf $(BUILDDIR) - rm -rf $(VLOGDIR) - rm -rf __pycache__ - rm -rf .pytest_cache - rm -rf logs - rm -f *.mem - -.PHONY: verilog clean + rm -rf $(BUILDDIR) $(VLOGDIR) $(TABDIR) __pycache__ .pytest_cache logs *.mem + +.PHONY: verilog table cocotb clean .DEFAULT_GOAL := cocotb diff --git a/test/cocotb/CrcAxiStreamTester.py b/test/cocotb/TestCrcAxiStream.py similarity index 64% rename from test/cocotb/CrcAxiStreamTester.py rename to test/cocotb/TestCrcAxiStream.py index 3ef34a1..596d738 100644 --- a/test/cocotb/CrcAxiStreamTester.py +++ b/test/cocotb/TestCrcAxiStream.py @@ -1,14 +1,16 @@ -import logging import os +import sys +import json import random +import logging from queue import Queue -import crc - +import crc import cocotb from cocotb.clock import Clock from cocotb.triggers import RisingEdge +import cocotb_test.simulator from cocotbext.axi import AxiStreamBus, AxiStreamSource, AxiStreamFrame from cocotbext.axi.stream import define_stream @@ -28,7 +30,8 @@ def __init__( cases_num: int, case_max_size: int, pause_rate: float, - ref_model: crc.Calculator, + crc_conf: crc.Configuration, + crc_mode: str, ): assert pause_rate < 1, "Pause rate is out of range" self.dut = dut @@ -41,7 +44,9 @@ def __init__( self.pause_rate = pause_rate self.ref_rawdata_buf = Queue(maxsize=self.cases_num) self.ref_checksum_buf = Queue(maxsize=self.cases_num) - self.ref_model = ref_model + self.crc_conf = crc_conf + self.ref_model = crc.Calculator(crc_conf) + self.crc_mode = crc_mode self.clock = self.dut.CLK self.reset = self.dut.RST_N @@ -83,6 +88,10 @@ def gen_random_test_case(self): data_size = random.randint(1, self.case_max_size) raw_data = random.randbytes(data_size) check_sum = self.ref_model.checksum(raw_data) + if self.crc_mode == "CRC_MODE_RECV": + crc_byte_num = int(self.crc_conf.width / 8) + zero_bytes = crc_byte_num * b"\x00" + raw_data = raw_data + zero_bytes return (raw_data, check_sum) async def drive_dut_input(self): @@ -117,3 +126,64 @@ async def runCrcAxiStreamTester(self): self.log.info("Start testing!") await check_thread self.log.info(f"Pass all {self.cases_num} successfully") + + +@cocotb.test(timeout_time=5000000, timeout_unit="ns") +async def runCrcAxiStreamTester(dut): + json_file_path = os.getenv("JSON_CONF_FILE") + with open(json_file_path) as json_file: + crc_config = json.load(json_file) + + crc_width = crc_config["crc_width"] + polynomial = int(crc_config["polynomial"], 16) + init_value = int(crc_config["init_value"], 16) + final_xor = int(crc_config["final_xor"], 16) + reverse_input = crc_config["reverse_input"] + reverse_output = crc_config["reverse_output"] + crc_mode = crc_config["crc_mode"] + + crc_conf = crc.Configuration( + width=crc_width, + polynomial=polynomial, + init_value=init_value, + final_xor_value=final_xor, + reverse_input=reverse_input, + reverse_output=reverse_output, + ) + tester = CrcAxiStreamTester( + dut=dut, + cases_num=1000, + case_max_size=1024, + pause_rate=0.3, + crc_conf=crc_conf, + crc_mode=crc_mode, + ) + await tester.runCrcAxiStreamTester() + + +def testCrcAxiStream(): + assert len(sys.argv) == 2 + json_file_path = sys.argv[1] + + # set parameters used to run tests + toplevel = "mkCrcRawAxiStreamCustom" + module = os.path.splitext(os.path.basename(__file__))[0] + test_dir = os.path.abspath(os.path.dirname(__file__)) + sim_build = os.path.join(test_dir, "build") + v_top_file = os.path.join(test_dir, "verilog", f"{toplevel}.v") + verilog_sources = [v_top_file] + extra_env = {"JSON_CONF_FILE": json_file_path} + + cocotb_test.simulator.run( + toplevel=toplevel, + module=module, + verilog_sources=verilog_sources, + python_search=test_dir, + sim_build=sim_build, + timescale="1ns/1ps", + extra_env=extra_env, + ) + + +if __name__ == "__main__": + testCrcAxiStream() diff --git a/test/cocotb/run_tests.py b/test/cocotb/run_tests.py new file mode 100644 index 0000000..d940351 --- /dev/null +++ b/test/cocotb/run_tests.py @@ -0,0 +1,64 @@ +import os +import sys +import json +from functools import reduce + + +def run_test(conf_file_path): + with open(conf_file_path) as json_file: + crc_config = json.load(json_file) + + crc_width = crc_config["crc_width"] + axi_keep_width = crc_config["axi_keep_width"] + polynomial = int(crc_config["polynomial"], 16) + init_value = int(crc_config["init_value"], 16) + final_xor = int(crc_config["final_xor"], 16) + reverse_input = crc_config["reverse_input"] + + if reverse_input: + reverse_input = "BIT_ORDER_REVERSE" + else: + reverse_input = "BIT_ORDER_NOT_REVERSE" + + reverse_output = crc_config["reverse_output"] + if reverse_output: + reverse_output = "BIT_ORDER_REVERSE" + else: + reverse_output = "BIT_ORDER_NOT_REVERSE" + + mem_file_prefix = crc_config["mem_file_prefix"] + crc_mode = crc_config["crc_mode"] + print( + f"Start Tests on: crc_width={crc_width} axi_keep_width={axi_keep_width} crc_mode={crc_mode}" + ) + + macros = [f"JSON_CONF_FILE={conf_file_path}"] + macros.append(f"CRC_WIDTH={crc_width}") + macros.append(f"AXI_KEEP_WIDTH={axi_keep_width}") + macros.append(f"POLY={polynomial}") + macros.append(f"INIT_VAL={init_value}") + macros.append(f"FINAL_XOR={final_xor}") + macros.append(f"REV_INPUT={reverse_input}") + macros.append(f"REV_OUTPUT={reverse_output}") + macros.append(f"MEM_FILE_PREFIX={mem_file_prefix}") + macros.append(f"CRC_MODE={crc_mode}") + + make_args = reduce(lambda x, y: x + " " + y, macros) + os.system("make clean") + result = os.system(f"make {make_args}") + return result + + +if __name__ == "__main__": + if len(sys.argv) == 2: + conf_file_path = sys.argv[1] + conf_file_path = os.path.abspath(conf_file_path) + run_test(conf_file_path) + else: + config_dir = os.path.abspath("../../scripts/config") + for root, dirs, files in os.walk(config_dir): + for file_name in files: + conf_file_path = os.path.join(root, file_name) + result = run_test(conf_file_path) + info = f"Testbench Failed on the configuration of {file_name}." + assert result == 0, info diff --git a/test/cocotb/run_tests.sh b/test/cocotb/run_tests.sh deleted file mode 100755 index de46d46..0000000 --- a/test/cocotb/run_tests.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -log_dir="logs" -crc_width_opt=(8 16 32) -axi_width_opt=(64 128 256 512) -protocol_opt=() -test_results=() -modules=() -pass_all=0 - -mkdir -p ${log_dir} - -for crc_width in ${crc_width_opt[@]}; do - for axi_width in ${axi_width_opt[@]}; do - module="mkCrc${crc_width}AxiStream${axi_width}" - log_file="${log_dir}/${module}.log" - echo "Generate Loopup Tables for ${module}" - python3 ../../src/gen_crc_tab.py ${crc_width} ${axi_width} - - echo "Generate verilog source files" - make verilog CRC_WIDTH=${crc_width} AXI_WIDTH=${axi_width} > ${log_file} - - echo "Start Cocotb Tests of ${module}" - python3 testCrcAxiStream.py ${crc_width} ${axi_width} >> ${log_file} - - test_results=(${test_results[@]} $?) - modules=(${modules[@]} ${module}) - rm -f *.mem - done -done - -echo "Complete All tests" -for ((i=0; i<${#test_results[@]}; i++)); -do - if [ ${test_results[i]} == 0 ] - then - echo "Pass tests of ${modules[i]}" - else - echo "Fail tests of ${modules[i]}" - pass_all=1 - fi -done - -return ${pass_all} \ No newline at end of file diff --git a/test/cocotb/testCrcAxiStream.py b/test/cocotb/testCrcAxiStream.py deleted file mode 100644 index 5385b4d..0000000 --- a/test/cocotb/testCrcAxiStream.py +++ /dev/null @@ -1,110 +0,0 @@ -import sys -import os -import crc -import cocotb_test.simulator -import cocotb - -from CrcAxiStreamTester import CrcAxiStreamTester - -CASES_NUM = 1000 -CASE_MAX_SIZE = 512 -PAUSE_RATE = 0.3 - -CRC8_WIDTH = 8 -CRC16_WIDTH = 16 -CRC32_WIDTH = 32 - -CRC8_CCITT_POLY = 0x07 -CRC16_ANSI_POLY = 0x8005 -CRC32_IEEE_POLY = 0x04C11DB7 - -CRC8_CCITT_INIT_VAL = 0x00 -CRC16_ANSI_INIT_VAL = 0x0000 -CRC32_IEEE_INIT_VAL = 0xFFFFFFFF - -CRC8_CCITT_FINAL_XOR = 0x00 -CRC16_ANSI_FINAL_XOR = 0x0000 -CRC32_IEEE_FINAL_XOR = 0xFFFFFFFF - - -@cocotb.test(timeout_time=5000000, timeout_unit="ns") -async def testCrc8AxiStream(dut): - crc_conf = crc.Configuration( - width=CRC8_WIDTH, - polynomial=CRC8_CCITT_POLY, - init_value=CRC8_CCITT_INIT_VAL, - final_xor_value=CRC8_CCITT_FINAL_XOR, - reverse_input=False, - reverse_output=False, - ) - ref_model = crc.Calculator(crc_conf) - tester = CrcAxiStreamTester(dut, CASES_NUM, CASE_MAX_SIZE, PAUSE_RATE, ref_model) - await tester.runCrcAxiStreamTester() - - -@cocotb.test(timeout_time=5000000, timeout_unit="ns") -async def testCrc16AxiStream(dut): - crc_conf = crc.Configuration( - width=CRC16_WIDTH, - polynomial=CRC16_ANSI_POLY, - init_value=CRC16_ANSI_INIT_VAL, - final_xor_value=CRC16_ANSI_FINAL_XOR, - reverse_input=True, - reverse_output=True, - ) - ref_model = crc.Calculator(crc_conf) - tester = CrcAxiStreamTester(dut, CASES_NUM, CASE_MAX_SIZE, PAUSE_RATE, ref_model) - await tester.runCrcAxiStreamTester() - - -@cocotb.test(timeout_time=5000000, timeout_unit="ns") -async def testCrc32AxiStream(dut): - crc_conf = crc.Configuration( - width=CRC32_WIDTH, - polynomial=CRC32_IEEE_POLY, - init_value=CRC32_IEEE_INIT_VAL, - final_xor_value=CRC32_IEEE_FINAL_XOR, - reverse_input=True, - reverse_output=True, - ) - ref_model = crc.Calculator(crc_conf) - tester = CrcAxiStreamTester(dut, CASES_NUM, CASE_MAX_SIZE, PAUSE_RATE, ref_model) - await tester.runCrcAxiStreamTester() - - -def testCrcAxiStream(): - crc_width_opt = (CRC8_WIDTH, CRC16_WIDTH, CRC32_WIDTH) - # Parse input arguments - assert len(sys.argv) == 3, "The number of input arguments is incorrect." - args = sys.argv - crc_width = int(args[1]) - assert ( - crc_width in crc_width_opt - ), f"Table generation of {crc_width}-bit hasn't been supported." - axi_width = int(args[2]) - assert axi_width % 8 == 0, f"The width of input data must be multiples of 8 bits." - axi_byte_num = int(axi_width / 8) - - # set parameters used to run tests - toplevel = f"mkCrc{crc_width}RawAxiStream{axi_width}" - test_func = f"testCrc{crc_width}AxiStream" - module = os.path.splitext(os.path.basename(__file__))[0] - test_dir = os.path.abspath(os.path.dirname(__file__)) - sim_build = os.path.join(test_dir, "build") - v_top_file = os.path.join(test_dir, "generated", f"{toplevel}.v") - verilog_sources = [v_top_file] - - cocotb_test.simulator.run( - toplevel=toplevel, - module=module, - verilog_sources=verilog_sources, - python_search=test_dir, - sim_build=sim_build, - timescale="1ns/1ps", - testcase=test_func, - work_dir=test_dir, - ) - - -if __name__ == "__main__": - testCrcAxiStream()