Task: Q3_CONV


---

# Version A — Human-Readable Spec (CONV)

## 1) Goal & Scope

Implement module **`TOP`** that:

* Reads a **64×64** grayscale image from the testfixture via the `iaddr/idata` interface. Pixel data width is **20 bits (Q4.16)**.  
* Produces:

  1. **Layer 0**: **zero-padding(1) → 3×3 convolution (Kernel 0) → +bias → ReLU** ⇒ **64×64** result in Q4.16, written to **L0\_MEM0**.    
  2. **Layer 1**: **2×2 max-pooling, stride 2** over Layer-0 output ⇒ **32×32** result in Q4.16, written to **L1\_MEM0**. 

---

## 2) Top-Level Ports (names, widths, meaning)

| Signal     | Dir | Width | Description                                                                                                                                                 |
| ---------- | --- | ----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clk`      | I   |     1 | System clock, **posedge-synchronous**.                                                                                                                      |
| `rst`    | I   |     1 | **Synchronous, active-high** rst.                                                                                                                        |
| `ready`    | I   |     1 | Testfixture asserts when inputs are prepared; `TOP` may start only after seeing `ready=1`.                                                                 |
| `busy`     | O   |     1 | Set **High** once to start work after seeing `ready=1`; set **Low** once when all required writes finish. Exactly **one rise** and **one fall** per image.  |
| `iaddr`    | O   |    12 | Address for input image (row-major, 0..4095).                                                                                                               |
| `idata`    | I   |    20 | Input pixel at `iaddr`, **Q4.16** (20-bit).                                                                                                                 |
| `crd`      | O   |     1 | Result memory **read enable** (see timing).                                                                                                                 |
| `cdata_rd` | I   |    20 | Read data from selected result memory (Q4.16).                                                                                                              |
| `caddr_rd` | O   |    12 | Read address.                                                                                                                                               |
| `cwr`      | O   |     1 | Result memory **write enable** (see timing).                                                                                                                |
| `cdata_wr` | O   |    20 | Write data (Q4.16).                                                                                                                                         |
| `caddr_wr` | O   |    12 | Write address.                                                                                                                                              |
| `csel`     | O   |     3 | Memory select: `3’b001` = **L0\_MEM0**, `3’b011` = **L1\_MEM0**.                                                                                            |

---

## 3) Handshake & Global Timing

1. **Start/End protocol**

   * After `rst` deasserts, testfixture sets `ready=1` when inputs (image & constants) are ready. On detecting `ready=1`, assert `busy=1` (start). When all mandated writes (L0 then L1) complete, deassert `busy` (end). **Exactly one** rising and **one** falling edge of `busy` per image.  

2. **Image memory read timing (`iaddr`→`idata`)**

   * Drive `iaddr` on/after a **posedge**; the testfixture returns the addressed pixel on `idata` **after the following negedge**. Keep `iaddr` stable until that negedge. (You may pipeline one address per cycle.) 

3. **Result memory timing (L0\_MEM0 & L1\_MEM0)**

   * **Read**: If `crd=1` at a **negedge**, `cdata_rd` immediately reflects the word at `caddr_rd` (for selected `csel`). 
   * **Write**: If `cwr=1` at a **posedge**, the testfixture writes `cdata_wr` to `caddr_wr` (for selected `csel`). 
   * **Select**: Use `csel=3’b001` to access **L0\_MEM0**; use `csel=3’b011` to access **L1\_MEM0**.  

---

## 4) Memories & Addressing

* **Input image**: 64×64 = **4096** pixels, row-major (`addr = y*64 + x`). Data width **20 bits (Q4.16)**.  
* **L0\_MEM0**: Layer-0 output, **64×64**, row-major, **20-bit Q4.16**. Access with `csel=3’b001`. Quantize per §5.  
* **L1\_MEM0**: Layer-1 output, **32×32**, row-major, **20-bit Q4.16**. Access with `csel=3’b011`. 

---

## 5) Numeric Format & Quantization

* All I/O and stored results are **20-bit Q4.16 two’s-complement** (4 integer bits incl. sign + 16 fractional bits). 
* **Round-to-nearest** when writing to memories using the **17th fractional bit**.

---

## 6) Layer Algorithms

### 6.1 Layer 0 — Zero-padding → 3×3 Convolution → +Bias → ReLU

* **Zero-padding**: Pad the input image with **1-pixel zeros** on all sides so that 3×3 stride-1 convolution preserves 64×64 size. 
* **Convolution** (stride 1):

  $$
  S(x,y)=\sum_{i=0}^{2}\sum_{j=0}^{2}K[i,j]\cdot I(x+i-1,\,y+j-1)
  $$

  where `I` is the padded image (Q4.16) and `K` is **Kernel 0** (Q4.16). After the sum, **add bias**, then **ReLU**: `S←max(S,0)`.  
* **Kernel 0 (decimal, Q4.16 constants)**:

  ```
  [ [  0.426810,  0.573572,  0.658674 ],
    [ -0.569037, -0.439696,  0.0625617],
    [ -0.327755, -0.217964, -0.348290] ]

   =

   [ [ 0x0A89E, 0x092D5, 0x06D43], 
     [ 0x01004, 0xF8F71, 0xF6E54], 
     [ 0xFA6D7, 0xFC834, 0xFAC19] ]
  ```

  (Hex Q4.16 provided in the brief as well.)  
* **Bias**: `+0.07446326` (decimal), **Q4.16** = `0x01310`. 
* **Write-out**: Quantize to Q4.16 using §5, then write each (x,y) to **L0\_MEM0** at row-major address with `csel=3’b001` and `cwr=1` on the write **posedge**.  

### 6.2 Layer 1 — Max-Pooling (2×2 window, stride 2)

* For each **2×2** window over the Layer-0 image, output the **maximum** of the four Q4.16 values. Scan left→right, top→bottom; output size is **32×32**. 
* **Write-out**: Write each pooled value (unchanged Q4.16) to **L1\_MEM0** at row-major address with `csel=3’b011` and `cwr=1` on the write **posedge**.  

---

## 7) Legality & Assumptions

* Only **one** start (`busy`↑ after `ready`=1) and **one** end (`busy`↓) per image. While `busy=1`, you may perform unlimited reads/writes to L0/L1. 
* Respect the **edge timing**: `iaddr` valid until the following **negedge** when `idata` returns; `crd` read on **negedge**; `cwr` write on **posedge**; `csel` selects the target memory for both read/write.  
* Internal micro-architecture is unconstrained; only the **I/O, timing, addressing, numeric**, and **algorithm** contracts are verified.

---


## Reference RTL Implementation

module  TOP(
	input			clk,
	input			rst,
	output reg		busy,	
	input			ready,				
	output reg[11:0]	iaddr,
	input signed[19:0]		idata,	
	output reg 	 	cwr,
	output reg[11:0] 	caddr_wr,
	output reg[19:0] 	cdata_wr,	
	output reg	 	crd,
	output reg[11:0]	caddr_rd,
	input	[19:0]		cdata_rd,
	output reg[2:0] 	csel
	);
//================================================================
//  Wires & Registers 
//================================================================
reg [5:0] x, y, L1_x, L1_y;
wire signed [39:0] bias;
assign bias = 40'h0013100000;

wire signed [19:0] conv_result;

reg [3:0] counter_data, counter_addr;
reg [2:0] counter_layer1;

reg [2:0] state_r, state_w;

localparam IDLE = 0, INPUT = 1, L0_MEM = 2, DELAY_CLK = 3, READ_L0_MEM = 4;
localparam L1_MEM = 5, DELAY_CLK_2 = 6, FINISH = 7;

localparam NOSEL = 3'b000, LAYER_0 = 3'b001, LAYER_1 = 3'b011;

//================================================================
//  iaddr 
//================================================================

always@( posedge clk or posedge rst ) begin
	if( rst ) iaddr <= 0;
	else if( state_w == INPUT ) begin
		case( counter_addr )
		4'd0: iaddr <= {y - 1'd1,x - 1'd1};
		4'd1: iaddr <= {y - 1'd1,x};
		4'd2: iaddr <= {y - 1'd1,x + 1'd1};
		4'd3: iaddr <= {y,x - 1'd1};
		4'd4: iaddr <= {y,x};
		4'd5: iaddr <= {y,x + 1'd1};
		4'd6: iaddr <= {y + 1'd1,x - 1'd1};
		4'd7: iaddr <= {y + 1'd1,x};
		4'd8: iaddr <= {y + 1'd1,x + 1'd1};
		endcase
	end
	else if( state_w == L0_MEM ) iaddr <= {y,x+1'd1};
end

//================================================================
//  x && y 
//================================================================
always@( posedge clk or posedge rst ) begin
	if( rst ) begin
		x <= 0; y <= 0;
	end
	else if( state_r == L0_MEM ) begin
		x <= x + 1; y <= ( x== 63 )? y + 1 : y;
	end
end

//================================================================
//  L1_x && L1_y
//================================================================
always@( posedge clk or posedge rst ) begin
	if( rst )  begin
		L1_x <= 0; L1_y <= 0;
	end
	else if( state_r == L1_MEM  ) begin
		L1_x <= ( L1_x == 62 )? 0 : L1_x + 2; L1_y <= ( L1_x == 62 )? L1_y + 2 : L1_y;	
	end
end


localparam Kernel_0 = 20'h0A89E,
		  Kernel_1 = 20'h092D5,
		  Kernel_2 = 20'h06D43,
		  Kernel_3 = 20'h01004,
		  Kernel_4 = 20'hF8F71,
		  Kernel_5 = 20'hF6E54,
		  Kernel_6 = 20'hFA6D7,
		  Kernel_7 = 20'hFC834,
		  Kernel_8 = 20'hFAC19;

reg signed [19:0] Kernel;	
	  
always@( * )
begin
	case( counter_data )
	4'd1: Kernel = Kernel_0;
	4'd2: Kernel = Kernel_1;
	4'd3: Kernel = Kernel_2;
	4'd4: Kernel = Kernel_3;
	4'd5: Kernel = Kernel_4;
	4'd6: Kernel = Kernel_5;
	4'd7: Kernel = Kernel_6;
	4'd8: Kernel = Kernel_7;
	4'd9: Kernel = Kernel_8;
	default: Kernel = 0;
	endcase
end

wire signed [39:0] data_conv;
reg  signed [39:0] data_conv_sum;
reg  signed [19:0] idata_tmp;
assign data_conv = idata_tmp * Kernel;
 
//================================================================
//  data_conv_sum && idata_tmp
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) idata_tmp <= 0;
	else if( state_r == INPUT ) begin
		case( counter_data )
		4'd0: idata_tmp <= ( x == 0 || y == 0 )? 0 : idata;
		4'd1: idata_tmp <= ( y == 0 )? 0: idata;
		4'd2: idata_tmp <= ( x == 63 || y == 0 )? 0: idata;	
		4'd3: idata_tmp <= ( x == 0 )? 0: idata;
		4'd4: idata_tmp <= idata;
		4'd5: idata_tmp <= ( x == 63 )? 0: idata;
		4'd6: idata_tmp <= ( x == 0 || y == 63 )? 0: idata;
		4'd7: idata_tmp <= ( y == 63 )? 0: idata;
		4'd8: idata_tmp <= ( x == 63 || y == 63 )? 0: idata;
		endcase
	end 
end

always@( posedge clk or posedge rst ) begin
	if( rst ) data_conv_sum <= 0;
	else if( state_r == INPUT ) begin
		if( counter_data == 0 ) data_conv_sum <= 0;
		else if( counter_data == 10 ) data_conv_sum <= data_conv_sum + bias;
		else data_conv_sum <= data_conv_sum + data_conv;
	end
end

//================================================================
//  cwr
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) cwr <= 0;
	else if( state_r == L0_MEM ) cwr <= 1;
	else if( state_r == L1_MEM ) cwr <= 1;
	else cwr <= 0;
end

//================================================================
//  crd
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) crd <= 0;
	else if( state_w == READ_L0_MEM ) crd <= 1;
	else crd <= 0;
end

//================================================================
//  caddr_rd
//================================================================ 

always@( posedge clk or posedge rst ) begin
	if( rst ) caddr_rd <= 0;
	else if( state_w == READ_L0_MEM )  begin
		case( counter_layer1 )
		3'd0: caddr_rd <= {L1_y,L1_x};
		3'd1: caddr_rd <= {L1_y,L1_x+1'd1};
		3'd2: caddr_rd <= {L1_y+1'd1,L1_x};
		3'd3: caddr_rd <= {L1_y+1'd1,L1_x+1'd1};
		endcase 
	end
end

//================================================================
//  caddr_wr
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) caddr_wr <= 0;
	else if( state_r == L0_MEM ) caddr_wr <= {y,x};
	else if( state_r == L1_MEM ) caddr_wr <= {L1_y[5:1],L1_x[5:1]};
end

//================================================================
//  cdata_wr
//================================================================ 
assign conv_result = ( data_conv_sum[15] )? {data_conv_sum[35:16]} + 1 : {data_conv_sum[35:16]}  ;

always@( posedge clk or posedge rst ) begin
	if( rst ) cdata_wr <= 0;
	else if( state_r == L0_MEM ) cdata_wr <= ( data_conv_sum[39] )? 0 : conv_result;
	else if( state_r == READ_L0_MEM ) begin
		if( counter_layer1 == 3'd1 ) cdata_wr <= cdata_rd;
		else cdata_wr <= ( cdata_rd > cdata_wr )? cdata_rd: cdata_wr; 
	end
end

//================================================================
//  counter_addr
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) counter_addr <= 0;
	else if( state_w == INPUT ) counter_addr <= counter_addr + 1;
	else if( state_w == L0_MEM ) counter_addr <= 0;
end

//================================================================
//  counter_data
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) counter_data <= 0;
	else counter_data <= counter_addr;
end

//================================================================
//  counter_layer1
//================================================================ 
always@( posedge clk or posedge rst ) begin
	if( rst ) counter_layer1 <= 0;
	else if( state_w == READ_L0_MEM ) counter_layer1 <= counter_layer1 + 1;
	else counter_layer1 <= 0;
end

//================================================================
//  busy 
//================================================================
always@( posedge clk or posedge rst )  begin
	if( rst ) busy <= 0;
	else if( ready ) busy <= 1; 
	else if( state_r == FINISH ) busy <= 0;
end

//================================================================
//  csel
//================================================================
always@( posedge clk or posedge rst ) begin
	if( rst ) csel <= NOSEL;
	else if( state_r == L0_MEM ) csel <= LAYER_0;
	else if( state_r == L1_MEM ) csel <= LAYER_1;
	else if( state_w == READ_L0_MEM ) csel <= LAYER_0;
	else csel <= NOSEL;
end

//================================================================
//  FSM
//================================================================
always@( posedge clk or posedge rst ) begin
	if( rst ) state_r <= IDLE;
	else state_r <= state_w;
end

always@( * ) begin
	case( state_r )
	IDLE: 		state_w = INPUT;
	INPUT: 		state_w = ( counter_data == 10 )? L0_MEM : INPUT ;
	L0_MEM:		state_w = DELAY_CLK;
	DELAY_CLK: 	state_w = ( x == 0 && y == 0 )? READ_L0_MEM : INPUT;
	READ_L0_MEM: 	state_w = ( counter_layer1 == 4 )? L1_MEM : READ_L0_MEM ;
	L1_MEM: 		state_w = DELAY_CLK_2;
	DELAY_CLK_2: 	state_w = ( L1_x == 0 && L1_y == 0 )? FINISH : READ_L0_MEM;
	FINISH: 		state_w = FINISH;
	default: 			state_w = IDLE;
	endcase
end

endmodule


## Testbench

`timescale 1ns/10ps
`define CYCLE      10.0          	  // Modify your clock period here
`define End_CYCLE  100000000              // Modify cycle times once your design need more cycle times!

`define PAT            "./00_TB/dat_grad/cnn_sti.dat"                 // Modify your "dat" directory path
`define L0_EXP0        "./00_TB/dat_grad/cnn_layer0_exp0.dat"       
`define L1_EXP0        "./00_TB/dat_grad/cnn_layer1_exp0.dat"     

module test;


reg	[19:0]	PAT	[0:4095];

reg	[19:0]	L0_EXP0	[0:4095];  
reg	[19:0]	L0_MEM0	[0:4095]; 

 
reg	[19:0]	L1_EXP0	[0:1023];
reg	[19:0]	L1_MEM0	[0:1023];



reg		rst = 0;
reg		clk = 0;
reg		ready = 0;

wire		cwr;
wire		crd;
wire	[19:0]	cdata_wr;
reg	[19:0]	cdata_rd;
wire	[2:0]	csel;
wire	[11:0]	caddr_rd;
wire	[11:0]	caddr_wr;

wire	[11:0]	iaddr;
reg	[19:0]	idata;


integer		p0, p1, p3, p4, p2;
integer		err00, err10;

integer		pat_num;
reg		check0=0, check1=0;

localparam SDFFILE = "../02_SYN/Netlist/top_syn.sdf";
`ifdef SDF
	initial $sdf_annotate(SDFFILE, TOP);
`endif

TOP TOP(
			.clk(clk),
			.rst(rst),
			.busy(busy),	
			.ready(ready),	
			.iaddr(iaddr),
			.idata(idata),
			.cwr(cwr),
			.caddr_wr(caddr_wr),
			.cdata_wr(cdata_wr),
			.crd(crd),
			.cdata_rd(cdata_rd),
			.caddr_rd(caddr_rd),
			.csel(csel)
			);
			


always begin #(`CYCLE/2) clk = ~clk; end

// initial begin
// 	$fsdbDumpfile("CONV.fsdb");
// 	$fsdbDumpvars(0, test);
// end
initial begin
	$display(" Cycle Period = %0f ns", `CYCLE);
end

// initial begin
// 	$dumpfile("CONV.vcd");
// 	$dumpvars(0, testfixture);
// end

initial begin  // global control
	$display("-----------------------------------------------------\n");
 	$display("START!!! Simulation Start .....\n");
 	$display("-----------------------------------------------------\n");
	@(negedge clk); #1; rst = 1'b1;  ready = 1'b1;
   	#(`CYCLE*3);  #1;   rst = 1'b0;  
   	wait(busy == 1); #(`CYCLE/4); ready = 1'b0;
end

initial begin // initial pattern and expected result
	wait(rst==1);
	wait ((ready==1) && (busy ==0) ) begin
		$readmemh(`PAT, PAT);
		$readmemh(`L0_EXP0, L0_EXP0);
		$readmemh(`L1_EXP0, L1_EXP0);
	end
		
end

always@(negedge clk) begin // generate the stimulus input data
	#1;
	if ((ready == 0) & (busy == 1)) idata <= PAT[iaddr];
	else idata <= 'hx;
	end


always@(negedge clk) begin
	if (crd == 1) begin
		case(csel)
			3'b001:cdata_rd <= L0_MEM0[caddr_rd] ;
			3'b011:cdata_rd <= L1_MEM0[caddr_rd] ;
		endcase
	end
end

always@(posedge clk) begin 
	if (cwr == 1) begin
		case(csel)
			3'b001: begin check0 <= 1; L0_MEM0[caddr_wr] <= cdata_wr; end
			3'b011: begin check1 <= 1; L1_MEM0[caddr_wr] <= cdata_wr; end 		
		endcase
	end
end

reg c1, c2;

//-------------------------------------------------------------------------------------------------------------------
initial begin  	// layer 0,  conv output
check0<= 0;
c1  = 0;
wait(busy==1); wait(busy==0);
if (check0 == 1) begin 
	err00 = 0;
	for (p0=0; p0<=4095; p0=p0+1) begin
		if (L0_MEM0[p0] == L0_EXP0[p0]) begin
			// $display(err00);
		end
		else begin
			err00 = err00 + 1;
			// $display(err00);
			begin 
				$display("WRONG! Layer 0 (Convolutional Output) with Kernel 0 , Pixel %d is wrong!", p0);
				$display("               The output data is %h, but the expected data is %h ", L0_MEM0[p0], L0_EXP0[p0]);
			end
		end
	end
	if (err00 == 0) $display("Layer 0 (Convolutional Output) with Kernel 0 is correct !");
	else		 $display("Layer 0 (Convolutional Output) with Kernel 0 be found %d error !", err00);
	c1 = 1;
end
end

//-------------------------------------------------------------------------------------------------------------------
initial begin  	// layer 1,  max-pooling output
check1<= 0;
c2 = 0;
wait(busy==1); wait(busy==0);
wait(c1 == 1);
if(check1 == 1) begin
	err10 = 0;
	for (p1=0; p1<=1023; p1=p1+1) begin
		if (L1_MEM0[p1] == L1_EXP0[p1]);// $display(err10);
		else begin
			err10 = err10 + 1;
			begin 
				$display("WRONG! Layer 1 (Max-pooling Output) with Kernel 0 , Pixel %d is wrong!", p1);
				$display("               The output data is %h, but the expected data is %h ", L1_MEM0[p1], L1_EXP0[p1]);
			end
		end
	end
	if (err10 == 0) $display("Layer 1 (Max-pooling Output) with Kernel 0 is correct!");
	else		 $display("Layer 1 (Max-pooling Output) with Kernel 0 be found %d error !", err10);
	c2 = 1;
end
end

//-------------------------------------------------------------------------------------------------------------------
initial  begin
 #`End_CYCLE ;
 	$display("-----------------------------------------------------\n");
 	$display("Error!!! The simulation can't be terminated under normal operation!\n");
 	$display("-------------------------FAIL------------------------\n");
 	$display("-----------------------------------------------------\n");
 	$finish;
end

reg out_end;

initial begin
	out_end = 0;
      wait(busy == 1);
      wait(busy == 0);      
	  wait(c1 == 1);
	  wait(c2 == 1);
    $display(" ");
	$display("-----------------------------------------------------\n");
	$display("--------------------- S U M M A R Y -----------------\n");
	if( (check0==1)&(err00==0) ) $display("Congratulations! Layer 0 data have been generated successfully! The result is PASS!!\n");
		else if (check0 == 0) $display("Layer 0 output was fail! \n");
		else $display("FAIL!!!  There are %d errors! in Layer 0 \n", err00);
	if( (check1==1)&(err10==0) ) $display("Congratulations! Layer 1 data have been generated successfully! The result is PASS!!\n");
		else if (check1 == 0) $display("Layer 1 output was fail! \n");
		else $display("FAIL!!!  There are %d errors! in Layer 1 \n", err10);
	if ((check0|check1) == 0) $display("FAIL!!! No output data was found!! \n");
	$display("-----------------------------------------------------\n");
    //   #(`CYCLE/2); $finish;
	out_end = 1;
end



integer exe_cycle;
integer total_error;

initial begin
	exe_cycle = 0;
	while(!out_end) begin
		exe_cycle = exe_cycle + 1;
		@(negedge clk);
	end
	total_error = err00 + err10;
	$display("----------------------------------------------");
  	$display("------------total time: %0d cycles-----------------", exe_cycle);
  	$display("----------------------------------------------");
	if(err00 == 0 && err10 == 0) begin
		$display("------------------------------------------------");
		$display("----------------All tests PASS!-----------------");
		$display("------------------------------------------------");
	end else begin
		$display("------------------------------------------------");
		$display("-              Total Error: %0d               -", total_error);
		$display("------------------------------------------------");
	end
	
	$finish;
end


   
endmodule