# Lab 3 Fir
[Code](https://github.com/Eltdot/SoC_Lab3_Fir)
## Introduction
[Workbook](https://drive.google.com/file/d/1PSqW4qURLvSZxRDxA4NAYPYXn8ixE6ib/view)
This design primarily implements a simple **convolution**:
$$
y[t] = \sum_{i = 0}^{\text{tap_num}-1} \left( h[i] \times x[t - i] \right)
$$
The main challenge lies in the fact that input and output data need to be transmitted via **AXI-Lite** and **AXI-Stream**, and **shift registers** cannot be used. The implementation must rely on only **32 DW of SRAM**, and the design allows for only one **32-bit adder** and one **32-bit multiplier**.
## Design Spec
- **Data_Width** 32bit (integer)
- **Tap_Num** max 32
- **# of Tap is a programmable parameter**
- Maximum storage of Tap and Data RAM is 32 DW
- **Data_Num** *programmable parameter* — Based on the size of the data file
- **Interface**
- **axilite**: configuration data_in:
- **axi-stream**: stream ( Xn ), stream ( Yn )
- **Using one Multiplier and one Adder:**
- **Note**: Multiplication and Addition are run in separate pipeline cycle
- **Note**: Don’t use DSP, use Xilinx directive `(* use_dsp = "no" *)`
- **Shift registers implemented with SRAM** ( Shift_RAM, size = 32 DW )
- **Tap coefficient implemented with SRAM** ( Tap_RAM = 32 DW ), initialized by axilite write
- **Operation**
1. Program `ap_start` to initiate FIR engine
2. Stream in `Xn`. The rate is varied by testbench. `axi-stream` tvalid/tready for flow control.
3. Stream out `Yn`, the rate of output transfer is varied by testbench. `axi-stream` tvalid/tready for flow control.
## AXI Transfer Protocal
### axi-lite write



In this design, only `awvalid`, `wvalid`, `awready`, and `wready` are used. It is important to note that a **single arrow (`→`)** indicates that these signals are related but does not imply a strict ordering. In contrast, a **double arrow (`⇒`)** denotes a sequential dependency, meaning that the referenced signal must be received before the next signal can be asserted.
### axi-lite read



Here, it is important to note that `RREADY` might only be asserted **after detecting** `RVALID`. Therefore, during the design process, care must be taken **not to use** `RREADY` as a condition for `RVALID`.
### axi-stream


In this case, `tvalid` and `tready` do not have a strict order of precedence. Therefore, in the design process, `tready` can be asserted **before or after** `tvalid` without any rigid constraints.
# Code Explanation
## FSM
### state_tap
```verilog
reg [1:0] state;
reg [1:0] next_state;
// localparam for state
localparam IDLE = 2'b00; // Initial
localparam TRAN = 2'b01; // Write or Read Tap, data_len, Tap_num
localparam WAIT = 2'b10;
localparam FIR = 2'b11; // do FIR
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
state <= IDLE;
end else begin
state <= next_state;
end
end
// next_state update
always @(*) begin
case (state)
IDLE: begin
next_state <= TRAN;
end
TRAN: begin
if (w_permit | r_permit) begin
next_state <= WAIT;
end else begin
next_state <= TRAN;
end
end
WAIT: begin
if (ap_start) begin
next_state <= FIR;
end else begin
next_state <= TRAN;
end
end
FIR: begin
if (ap_done) begin
next_state <= TRAN;
end else begin
next_state <= FIR;
end
end
default: begin
next_state <= IDLE;
end
endcase
end
```
```mermaid
stateDiagram-v2
[*] --> IDLE
IDLE --> TRAN
TRAN --> WAIT : w_permit | r_permit
TRAN --> TRAN : !(w_permit | r_permit)
WAIT --> FIR : ap_start
WAIT --> TRAN : !ap_start
FIR --> TRAN : ap_done
FIR --> FIR : !ap_done
```
`state_tap` is primarily responsible for managing **AXI-Lite** operations and consists of four states: **IDLE, TRAN, WAIT, and FIR**.
#### **IDLE**
The system enters the `IDLE` state whenever it is reset or after completing a full round of FIR computation.
In this state, the system does nothing, and in the next cycle, it transitions to the `TRAN` state.
#### **TRAN**
The `TRAN` state is used for inputting various coefficients for `tap_number`.
In this state, **read and write operations** can be performed.
After receiving a read or write signal, the system transitions to the `WAIT` state.
#### **WAIT**
The system enters the `WAIT` state after receiving a read or write signal in the `TRAN` state.
In this state, the corresponding **ready signal** is asserted.
If `ap_start` is asserted during this phase, the system transitions to the `FIR` state, where FIR computation begins.
#### **FIR**
`FIR` is the state where the system performs FIR computation.
In this state, **writing to any register or reading tap coefficients is not allowed**.
If such an operation is attempted, it will either be ignored or return `32'hffffffff`.
If the system reads from address `12'h0` in this state, it indicates that a full round of data processing has been completed, and the system transitions back to `IDLE`, preparing for the next round.
> **(Potential Bug Warning)**
> If the previous round has not yet finished but `12'h0` is read prematurely, the system will transition to `IDLE`, preventing FIR from continuing. This behavior needs further verification.
### state_data
```verilog
reg [1:0] state;
reg [1:0] next_state;
// state for data
localparam IDLE = 2'b00;
localparam TRAN = 2'b01;
localparam FIR = 2'b10;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
state <= IDLE;
end else begin
state <= next_state;
end
end
always @(*) begin
case (state)
IDLE: begin
if (ap_start) begin
next_state = TRAN;
end else begin
next_state = IDLE;
end
end
TRAN: begin
if (ss_tvalid) begin
next_state = FIR;
end else begin
next_state = TRAN;
end
end
FIR: begin
if (sm_tvalid & sm_tready) begin
if (sstlast_reg) begin
next_state = IDLE;
end else begin
next_state = TRAN;
end
end else begin
next_state = FIR;
end
end
default: begin
next_state = IDLE;
end
endcase
end
```
```mermaid
stateDiagram-v2
[*] --> IDLE
IDLE --> TRAN : ap_start
IDLE --> IDLE : !ap_start
TRAN --> FIR : ss_tvalid
TRAN --> TRAN : !ss_tvalid
FIR --> IDLE : sm_tvalid & sm_tready & sstlast_reg
FIR --> TRAN : sm_tvalid & sm_tready & !sstlast_reg
FIR --> FIR : !(sm_tvalid & sm_tready)
```
`state_data` is primarily responsible for managing the **AXI-Stream** operations.
It consists of three states: **IDLE, TRAN, and FIR**.
#### **IDLE**
The system remains in the `IDLE` state when `ap_start` has not yet been received.
In this state, **signals generated by AXI-Stream cannot be received**.
Once `ap_start` is asserted, the system transitions to the `TRAN` state.
#### **TRAN**
The `TRAN` state is active when the system has **not yet received** `ss_tvalid`.
Once the system receives a valid `ss_tvalid` and `ss_tdata`, it transitions to the `FIR` state.
#### **FIR**
The `FIR` state is entered after the system receives `ss_tvalid`.
In this state, the system performs the computation corresponding to the current input data.
Once the computation is complete and the output has been consumed, the system determines whether this is the **last data entry**:
- If **it is the last entry**, the system transitions to the `IDLE` state to prepare for the next round.
- If **it is not the last entry**, the system returns to the `TRAN` state and waits for the next `ss_tdata` input.
### state_ap
```verilog
reg [1:0] state_ap;
reg [1:0] next_state_ap;
// state_ap
localparam IDLE_AP = 2'b00;
localparam CAL_AP = 2'b01;
localparam DONE_AP = 2'b10;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
state_ap <= IDLE_AP;
end else begin
state_ap <= next_state_ap;
end
end
always @(*) begin
case (state_ap)
IDLE_AP: begin
if (ap_start) begin
next_state_ap = CAL_AP;
end else begin
next_state_ap = IDLE_AP;
end
end
CAL_AP: begin
if (sm_tvalid & sm_tready & sstlast_reg) begin
next_state_ap = DONE_AP;
end else begin
next_state_ap = CAL_AP;
end
end
DONE_AP: begin
if (r_permit & (address_reg == 12'h0) & arready) begin
next_state_ap = IDLE_AP;
end else begin
next_state_ap = DONE_AP;
end
end
default: begin
next_state_ap = IDLE_AP;
end
endcase
end
```
```mermaid
stateDiagram-v2
[*] --> IDLE_AP
IDLE_AP --> CAL_AP : ap_start
IDLE_AP --> IDLE_AP : !ap_start
CAL_AP --> DONE_AP : sm_tvalid & sm_tready & sstlast_reg
CAL_AP --> CAL_AP : otherwise
DONE_AP --> IDLE_AP : r_permit & (address_reg == 0x0) & arready
DONE_AP --> DONE_AP : otherwise
```
`IDLE_AP` refers to the state where the system resides before `ap_start` is asserted. After every reset, the system also returns to this state.
If `ap_start` is asserted while in the `IDLE_AP` state, the state machine transitions to the `CAL_AP` state.
`CAL_AP` refers to the state where the system is performing FIR computations.
If the system receives `sm_tvalid & sm_tready & sstlast_reg`, it indicates that the system has received the final `y` value, and the system will transition to the `DONE_AP` state.
`DONE_AP` refers to the state where the system has completed one full round of data processing and all `y` values have been output.
At this point, if the system detects `address == 12'h0`, it can transition back to the `IDLE_AP` state, allowing the system to proceed to the next round.
```verilog
assign ap_done = (state_ap == DONE_AP);
assign ap_idle = (state_ap != CAL_AP);
```
`ap_done` and `ap_idle` can be directly controlled by `state_ap`.
`ap_idle` is only **0** when FIR processing is in progress, while `ap_done` is **1** only in the `DONE_AP` state.
Once `ap_done` is read, `state_ap` will transition to the `IDLE_AP` mode, and `ap_done` should also be set to **0**.
```verilog
assign ap_start = (w_permit & (address_reg == 12'h0)) ? data_reg : ((state == FIR) ? 0 : ap_start_prev);
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
ap_start_prev <= 0;
end else begin
ap_start_prev <= ap_start;
end
end
```
`ap_start` is written when `address` is **0** and `w_permit` is asserted.
It is reset to **0** when `state_ap` and `state_tap` transition to FIR computation.
At all other times, it retains its previous value.
## axi-lite
### address_reg and data_reg
```verilog
reg [(pADDR_WIDTH-1):0] address_reg_prev;
reg [(pDATA_WIDTH-1):0] data_reg_prev;
reg [(pDATA_WIDTH-1):0] data_reg;
reg [(pADDR_WIDTH-1):0] address_reg;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
address_reg_prev <= address_reg;
data_reg_prev <= data_reg;
end else begin
address_reg_prev <= address_reg;
data_reg_prev <= data_reg;
end
end
always @(*) begin
case (state)
IDLE: begin
address_reg = 0;
data_reg = 0;
end
TRAN: begin
if (awvalid) begin
address_reg = awaddr;
end else if (arvalid) begin
address_reg = araddr;
end else begin
address_reg = address_reg_prev;
end
if (wvalid) begin
data_reg = wdata;
end else begin
data_reg = data_reg_prev;
end
end
WAIT: begin
address_reg = address_reg_prev;
data_reg = data_reg_prev;
end
FIR: begin
if (arvalid) begin
address_reg = araddr;
end else begin
address_reg = address_reg_prev;
end
data_reg = data_reg_prev;
end
default: begin
address_reg = 0;
data_reg = 0;
end
endcase
end
```
`address_reg` and `data_reg` are registers that store the addresses or data transmitted via **AXI-Lite**.
Their signals are determined based on the system state and whether a **write** or **read** operation is being performed.
### data_len_reg and tap_num_reg
```verilog
reg [(pDATA_WIDTH-1):0] tap_num_reg_prev;
reg [(pDATA_WIDTH-1):0] data_len_reg_prev;
wire [(pDATA_WIDTH-1):0] tap_num_reg;
wire [(pDATA_WIDTH-1):0] data_len_reg;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
data_len_reg_prev <= 0;
tap_num_reg_prev <= 0;
end else begin
data_len_reg_prev <= data_len_reg;
tap_num_reg_prev <= tap_num_reg;
end
end
assign data_len_reg = (w_permit & (address_reg == 12'h10)) ? data_reg : data_len_reg_prev;
assign tap_num_reg = (w_permit & (address_reg == 12'h14)) ? data_reg : tap_num_reg_prev;
```
`data_len_reg` and `tap_num_reg` store the data written to input addresses **0x10** and **0x14**, respectively.
### wready awready rvalid arready
```verilog
assign writing = awvalid & wvalid;
assign w_permit = writing & (state == TRAN);
assign r_permit = arvalid & (state != IDLE);
assign rvalid = (arready ? 1 : (rvaild_down ? 0 : rvalid_reg));
assign arready = (~(writing) & arvalid) & (state == WAIT | fir_read_d1);
assign awready = writing & ((state == WAIT) | (state == FIR));
assign wready = writing & ((state == WAIT) | (state == FIR));
assign fir_read_ok = (state == FIR) & arvalid;
always @(posedge axis_clk) begin
fir_read_d1 <= fir_read;
end
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
rvaild_down <= 0;
end else if (rvalid & rready) begin
rvaild_down <= 1;
end else begin
rvaild_down <= 0;
end
end
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
rvalid_reg <= 0;
end else begin
rvalid_reg <= rvalid;
end
end
```
- `AWREADY` and `WREADY` are asserted (`1`) when both `awvalid` and `wvalid` are high simultaneously.
They are designed to be asserted **one cycle after** `awvalid` and `wvalid`.
- `ARREADY` is asserted when `arvalid` is high and the system is **not in a write operation**.
It is also designed to be asserted **one cycle after** `arvalid`.
- `RVALID` is asserted **after** `arready` is received.
It remains high until `rready` is asserted, at which point it is deasserted (`0`).
### Tap Ram
```verilog
assign tap_WE = {4{(state == TRAN) & (awaddr[7] & (awaddr[11:8] == 0))}};
assign tap_EN = (state != IDLE);
assign tap_A = (state == FIR) ? {4'b0, fir_data_count, 2'b0} : (((w_permit | r_permit) & (address_reg[7] & (address_reg[11:8] == 0))) ? {5'b0, address_reg[6:2], 2'b0} : tap_A_prev);
assign tap_Di = (w_permit & (address_reg[7] & (address_reg[11:8] == 0))) ? data_reg : tap_Di_prev;
reg [(pADDR_WIDTH-1):0] tap_A_prev;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
tap_A_prev <= 0;
end else begin
tap_A_prev <= tap_A;
end
end
reg [(pDATA_WIDTH-1):0] tap_Di_prev;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
tap_Di_prev <= 0;
end else begin
tap_Di_prev <= tap_Di;
end
end
```
- `tap_WE` is enabled **only when** `state_tap` is in `TRAN` and `awaddr` is within the range `12'h80 ~ 12'hFF`.
- `tap_EN` is designed to be `1` in all `state_tap` states **except** `IDLE`.
- `tap_A` is divided into two parts to accommodate both **AXI-Lite** operations and **FIR computation**:
- If `state_tap == FIR`, `tap_A` is controlled by `fir_data_count` to ensure correct FIR operation.
- If `state_ap != FIR`, `tap_A` changes based on read/write requests. Otherwise, it retains its previous value.
- `tap_Di` is enabled **only when AXI-Lite needs to write data**. If no write operation is required, it retains its previous value.
### rdata
```verilog
reg [(pDATA_WIDTH-1):0] read_data_reg;
assign rdata = read_data_reg;
always @(*) begin
if (r_permit) begin
if (address_reg == 12'h0) begin
read_data_reg = ap_crtl;
end else if (address_reg == 12'h10) begin
read_data_reg = data_len_reg;
end else if (address_reg == 12'h14) begin
read_data_reg = tap_num_reg;
end else if ((address_reg[7] & (address_reg[11:8] == 0)) & state != FIR) begin
read_data_reg = tap_Do;
end else begin
read_data_reg = 32'hffffffff;
end
end else begin
read_data_reg = read_data_reg_prev;
end
end
endmodule
reg [(pDATA_WIDTH-1):0] read_data_reg_prev;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
read_data_reg_prev <= 0;
end else begin
read_data_reg_prev <= read_data_reg;
end
end
```
`read_data_reg` is a register used to retrieve the value from the specified address.
- If the read address is **not used in this design** or if **tap coefficients are read during FIR computation**, it returns `32'hffffffff`.
- Otherwise, it retains its previous value.
## axi-stream
### ss_tready
```verilog
assign ss_tready = (state == IDLE) ? 0 : ss_tready_reg;
wire ss_tready_reg_tmp;
assign ss_tready_reg_tmp = (state == TRAN) & ss_tvalid;
always @(posedge axis_clk) begin
ss_tready_reg <= ss_tready_reg_tmp;
end
```
`ss_tready` remains **0** when `state_data == IDLE`.
- It is asserted **one cycle after** receiving `ss_tvalid` when `state_data == TRAN`.
- It stays high for **only one cycle** before being deasserted.
### ss_tlast_reg
```verilog
reg sstlast_reg;
reg sstlast_reg_prev;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
sstlast_reg_prev <= 0;
end else begin
sstlast_reg_prev <= sstlast_reg;
end
end
always @(*) begin
case (state)
IDLE: begin
sstlast_reg = 0;
end
TRAN: begin
if (ss_tvalid & ss_tlast) begin
sstlast_reg = 1;
end else begin
sstlast_reg = 0;
end
end
default: begin
sstlast_reg = sstlast_reg_prev;
end
endcase
end
```
`ss_tlast_reg` is a register used to store whether the current data is the **last data entry**.
It serves as a reference when outputting `y` to determine whether the current round should end.
### total_num_data tape_num
```verilog
wire [5:0] tape_num;
wire [5:0] total_num_data_tmp;
assign total_num_data_tmp = (state == IDLE) ? 0 : (total_num_data == tape_num ? total_num_data : (ss_tready) ? (total_num_data + 1) : total_num_data);
assign tape_num = (tap_num_reg - 1) & 6'b111111;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
total_num_data <= 0;
end else begin
total_num_data <= total_num_data_tmp;
end
end
```
- `tap_num` records the **tap_number count**, using **0-based indexing**.
- `total_num_data` keeps track of the total number of data entries received in the current round.
- If `total_num_data` reaches `tap_num`, it remains fixed at `tap_num`.
### this_round_num base_addr next_sstdata_addr
```verilog
assign this_round_num = ((state == TRAN) & (ss_tvalid)) ? total_num_data : this_round_num_prev;
assign base_addr = (state == TRAN) ? next_sstdata_addr : base_addr_prev;
reg [5:0] next_sstdata_addr_tmp;
always @(*) begin
if (state == IDLE) begin
next_sstdata_addr_tmp = 0;
end else if ((state == FIR) & ss_tready) begin
if (next_sstdata_addr == tape_num) begin
next_sstdata_addr_tmp = 0;
end else begin
next_sstdata_addr_tmp = next_sstdata_addr + 1;
end
end else begin
next_sstdata_addr_tmp = next_sstdata_addr;
end
end
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
next_sstdata_addr <= 0;
end else begin
next_sstdata_addr <= next_sstdata_addr_tmp;
end
end
```
- `this_round_num` records the **number of data entries used in the current round** during the `TRAN` state.
- `base_addr` is updated **in the `TRAN` state** to store the **starting address** of data for the current round.
- `next_sstdata_addr` is updated **in the `FIR` state** to store the **starting address** of data for the next round.
### data_addr_gen fir_data_count
```verilog
reg [5:0] data_addr_gen;
always @(*) begin
case (state)
TRAN: begin
data_addr_gen = base_addr;
end
FIR: begin
data_addr_gen = (base_addr >= fir_data_count) ? (base_addr - fir_data_count) : (tap_num_reg + base_addr - fir_data_count);
end
default: begin
data_addr_gen = 0;
end
endcase
end
reg [5:0] fir_data_count
wire [5:0] fir_data_count_tmp;
assign fir_data_count_tmp = (state == FIR) ? fir_data_count + 1 : 0;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
fir_data_count <= 0;
end else begin
fir_data_count <= fir_data_count_tmp;
end
end
```
- `data_addr_gen` generates the corresponding **address** based on the **starting address** and the **current data index**.
- If `count > base_addr`, `tap_num` is added to correctly map to the actual address.
- `fur_data_count` is a register that records the **current data index** when the system transitions to the `FIR` state.
- It is used to **control `data_address` and `tap_address`**.
- It also determines when to **clear the `y` register**.
### countdown
```verilog
wire [31:0] countdown_tmp;
assign countdown_tmp = (state != FIR) ? 32'hffffffff : ((fir_data_count == this_round_num) ? 3 : countdown - 1);
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
countdown <= 32'hffffffff;
end else begin
countdown <= countdown_tmp;
end
end
```
`countdown` is used to determine when to retrieve the `y` value.
When `countdown` reaches **0**, `y` should be retrieved and wait for `sm` to read it.
### data Ram
```verilog
assign data_EN = (state != IDLE);
assign data_WE = {4{ss_tready}};
assign data_A = {4'b0, data_addr_gen, 2'b0};
assign data_Di = ss_tdata;
```
- `data_EN` is asserted **after `ap_start` is received**.
- Data can only be input when `ss_tready` is high, so `data_WE` is **asserted only when `ss_tready` is active**.
- `data_A` controls which data is being read or written and is **directly controlled by `data_addr_gen`**.
- `data_Di` is **directly connected to `ss_tdata`**, as `data_WE` is only asserted when `ss_tready` is high.
### sm_tvalid sm_tdata sm_tlast
```verilog
assign sm_tdata = sm_tdata_reg;
assign sm_tlast = (sm_tvalid) & (sstlast_reg);
always @(*) begin
if (state != FIR) begin
sm_tvalid = 0;
sm_tdata_reg = 0;
end else if (countdown == 0) begin
sm_tvalid = 1;
sm_tdata_reg = y_tmp_reg;
end else begin
sm_tvalid = sm_tvalid_prev;
sm_tdata_reg = sm_tdata_reg_prev;
end
end
reg [(pDATA_WIDTH-1):0] sm_tdata_reg_prev;
reg sm_tvalid_prev;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
sm_tdata_reg_prev <= 0;
sm_tvalid_prev <= 0;
end else begin
sm_tdata_reg_prev <= sm_tdata_reg;
sm_tvalid_prev <= sm_tvalid;
end
end
```
- `sm_tdata` retrieves the value from the `y` register **when `countdown` reaches 0**.
- It maintains the same value **until it is read**.
- `sm_tvalid` behaves the same as `sm_tdata`:
- It is asserted **when `countdown` reaches 0** and remains high **until `sm_tready` is received**.
- `sm_tlast` is asserted **when both `sstlast_reg` and `sm_tvalid` are high**.
- Since `sstlast_reg == 1` indicates that the **last data input has been received**, this output is also the **final data entry**.
- `sm_tlast` is asserted to notify the system.
## FIR Processing Flow
```verilog
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
data_tmp <= 0;
end else begin
data_tmp <= data_Do;
end
end
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
tap_tmp <= 0;
end else begin
tap_tmp <= tap_Do;
end
end
assign Multi_tmp = data_tmp * tap_tmp;
always @(posedge axis_clk or negedge axis_rst_n) begin
if (~axis_rst_n) begin
Multi_tmp_reg <= 0;
end else begin
Multi_tmp_reg <= Multi_tmp;
end
end
assign y_tmp = Multi_tmp_reg + y_tmp_reg;
always @(posedge axis_clk) begin
if (fir_data_count == 2) begin
y_tmp_reg <= 0;
end else begin
y_tmp_reg <= y_tmp;
end
end
```

### **Important Note**
When `fir_data_count == 2`, `y_tmp` will be **cleared**.
- This is because, in the next cycle, **the first value of a new round** will enter the `y` register.
- Therefore, clearing `y_tmp` at this point is necessary to ensure proper data handling.

The waveform diagram above represents the case when there is only **one data**.
- As shown, when `fir_data_count == 2`, `y_tmp_reg` needs to be **cleared**.
- This is because, on the next **posedge of `axis_clk`**, a new round's `y_tmp` will be stored.
# Waveform and Area Report
## Axilite

## Axistream

## Area Report

## Throuput
