Task: Q1_LBP

# Human-Readable Spec (LBP Accelerator)

## 1. Goal & Scope

Design an RTL hardware module **LBP** that:

* Reads an **8-bit** grayscale image from a host memory interface (**gray\_mem**, 128×128, linear address 0…16383).
* Computes **Local Binary Pattern (LBP)** for every pixel independently using a **3×3, 8-neighbor** definition.
* Writes each 8-bit result to a host memory interface (**lbp\_mem**, 128×128, address-aligned 1:1 with the source pixel).
* Sets **`finish=1`** when all required pixels are written.
* Excludes appendices and scoring/benchmark rules—this is a **functional** spec sufficient to implement and verify correctness.

## 2. Top-Level Module & Ports

**Module name:** `TOP`
**Clocking:** rising-edge synchronous (`clk`).
**rst:** `rst` is **active-high Synchronous**; design must reach a defined idle state after deassertion.

**Ports**

* `clk` (input, 1b): system clock, positive-edge synchronous.
* `rst` (input, 1b): Synchronous, active-high; initialize internal state when asserted; leave in a clean idle when deasserted.
* `gray_addr` (output, **14b**): read address to `gray_mem` (0…16383). **At most one address may be requested per clock cycle.**
* `gray_req` (output, 1b): read-request enable for `gray_mem`.
* `gray_ready` (input, 1b): host indicates `gray_mem` can accept requests. **Do not issue reads unless `gray_ready=1`.** If it ever deasserts, pause issuing new requests until it reasserts.
* `gray_data` (input, **8b**): data returned by host for the address requested.
* `lbp_addr` (output, **14b**): write address to `lbp_mem` (0…16383).
* `lbp_valid` (output, 1b): write-enable; when high, `lbp_addr` and `lbp_data` are valid and must be written by the host.
* `lbp_data` (output, **8b**): LBP result to be written.
* `finish` (output, 1b): assert high when the full frame is processed and all required writes have been performed; keep high until rst.

> **Interfaces:** `gray_mem` is read-only; `lbp_mem` is write-only. There is **no** `lbp_ready` input.

## 3. Image, Addressing & Mapping

* **Input image:** 128×128 pixels, each 8-bit unsigned (0…255), stored in `gray_mem`.
* **Output image:** 128×128 pixels, each 8-bit unsigned (0…255), stored in `lbp_mem`.
* **Linear address mapping (row-major):**
  `addr = y * 128 + x`, where `x,y ∈ [0,127]` and therefore `addr ∈ [0,16383]`.
* **1:1 address rule:** The LBP result derived from `gray_mem[k]` must be written to **`lbp_mem[k]`** (same `x,y` → same address).
* **Border rule:** The outermost ring (`x=0`, `x=127`, `y=0`, `y=127`) **must be 0** in `lbp_mem`. The host **pre-initializes** `lbp_mem` to 0. Your design may either:

  * Skip writing border addresses (preferred), or
  * Write explicit zeros to border addresses,
    but **must not** write any non-zero value at the border.

## 4. Read & Write Handshakes (with timing semantics)

**General:** Your RTL is rising-edge synchronous. The host side *acts* on the **negative edge** of the same clock.

### 4.1 Read (LBP → `gray_mem`)

* Issue a read by driving `gray_req=1` and a stable `gray_addr`.
* On the **next falling edge**, the host detects `gray_req=1` and places the byte at `gray_mem[gray_addr]` onto `gray_data`.
* Your RTL should **sample `gray_data`** on the **next rising edge** after that falling edge.
* **Burst reads**: keep `gray_req=1` and update `gray_addr` every cycle to stream one byte per cycle.
* **Pause**: deassert `gray_req`; host stops returning data from the next falling edge onward.
* **Constraint:** at most **one** read address may be issued per clock cycle. There is **no limit** on total requests over the whole run.

### 4.2 Write (LBP → `lbp_mem`)

* To write a result, drive `lbp_valid=1` with stable `lbp_addr` and `lbp_data` in the same cycle.
* On the **next falling edge**, the host performs the write to `lbp_mem[lbp_addr] = lbp_data`.
* **Burst writes**: hold `lbp_valid=1` and update `lbp_addr`/`lbp_data` each cycle to stream one byte per cycle.
* **Stop**: deassert `lbp_valid` to pause writes.

### 4.3 Start & Finish

* Do **not** issue reads until `gray_ready=1` has been observed after rst deassertion.
* After all required pixels are written to `lbp_mem` (respecting border rule and address mapping), assert `finish=1`.

## 5. LBP Algorithm (3×3, 8 neighbors)

Let `gc` be the center pixel; `gp` for neighbors `p=0..7`. Use the **row-major** 3×3 labeling below and weight each neighbor by `2^p`.

```
p=0  p=1  p=2        (-1,-1) (-1, 0) (-1,+1)
p=3  gc   p=4   ==   ( 0,-1) ( 0, 0) ( 0,+1)
p=5  p=6  p=7        (+1,-1) (+1, 0) (+1,+1)
```

**Definition**

```
bit_p = 1 if (gp - gc) >= 0 else 0
LBP(x,y) = sum_{p=0..7} bit_p << p
```

* Each pixel’s LBP is **independent** of others (suitable for internal parallelism).
* The LBP value naturally fits in 8 bits (0…255); no saturation or sign handling is required.

## 6. Iteration Domain & Control

* **Compute only for non-border pixels:** `x ∈ [1,126]`, `y ∈ [1,126]`.
* For each `(x,y)` in that range:

  1. Gather the 3×3 window around `(x,y)` using the address mapping.
  2. Compute `LBP(x,y)` per Section 5.
  3. Write the result to `lbp_mem[ y*128 + x ]`.
* **Order freedom:** You may process pixels in any order (scanline, tiled, micro-pipelined…), provided all protocol rules and mapping constraints are satisfied.

## 7. rst & Initialization Requirements

* Asserting `rst=1` must Synchronously clear internal state and outputs to safe defaults:

  * `gray_req=0`, `lbp_valid=0`, `finish=0`.
  * Optional: clear internal FIFOs/buffers.
* After deasserting `rst`, wait for `gray_ready=1` before starting reads.

## 8. Legal/Illegal Behaviors (Protocol Safety)

**Legal**

* Issue at most one read request per cycle; stream reads/writes by holding enable high.
* Pause/resume cleanly by dropping `gray_req`/`lbp_valid`.
* Skip border writes (since host pre-zeros the frame).

**Illegal**

* Issuing read requests while `gray_ready=0`.
* Writing any **non-zero** to border addresses.
* Writing a result to an address different from the source pixel’s address (`1:1` rule).

## 9. Reference Pseudocode (behavioral, cycle-agnostic)

```c
// Assume gray(y,x) reads gray_mem[y*128 + x]
// and lbp(y,x) writes lbp_mem[y*128 + x].

for (int y = 1; y <= 126; ++y) {
  for (int x = 1; x <= 126; ++x) {
    uint8_t gc = gray(y, x);
    uint8_t g[8] = {
      gray(y-1,x-1), gray(y-1,x),   gray(y-1,x+1),
      gray(y,  x-1),                 gray(y,  x+1),
      gray(y+1,x-1), gray(y+1,x),   gray(y+1,x+1)
    };
    uint8_t v = 0;
    for (int p=0; p<8; ++p) v |= ((g[p] >= gc) ? 1 : 0) << p;
    lbp(y, x) = v;
  }
}
// border rows/cols remain 0 in lbp_mem.
```

## 10. Implementation Notes (non-normative)

* A sliding 3×3 window with **two line buffers** (for previous rows) is typical; still, external protocol limits you to **one** read address per cycle.
* You may pipeline comparison and bit-packing; write as soon as the byte is ready.
* Any internal parallelism is allowed if external protocol, mapping, and outputs are preserved.

---


## Reference RTL Implementation

`timescale 1ns/10ps
module TOP ( clk, rst, gray_addr, gray_req, gray_ready, gray_data, lbp_addr, lbp_valid, lbp_data, finish);
input   	clk;
input   	rst;

// ask data 
output  reg       	gray_req;
output  reg [13:0] 	gray_addr;
input   	    gray_ready;
input   [7:0] 	gray_data;

// write data
output  reg [13:0] 	lbp_addr;
output  reg 	    lbp_valid;
output  reg [7:0] 	lbp_data;
output  reg  	    finish;

reg [7:0] buffer [8:0];
reg [1:0] state_r, state_w;

wire [13:0] minus = gray_addr - 255;
wire [13:0] plus  = gray_addr + 128;
wire valid = (lbp_addr[6:0]== 7'b111_1111 || lbp_addr[6:0]== 7'b000_0000);
wire fin = (lbp_addr==14'd16254);

localparam 	DATA1 = 0,
			DATA2 = 1,
			DATA3 = 2,
			WRITE_DATA   = 3;

always @(posedge clk or posedge rst) begin
	if (rst) begin
		state_r   <= DATA1;
		gray_addr <= 0;
		lbp_addr  <= 126;
		finish    <= 0;
	end
	else begin
		state_r  <= state_w;
		gray_req <= 1;
		case(state_r)
			DATA1:begin
				lbp_valid <= 0;
				gray_addr <= plus;
				buffer[2] <= gray_data;     // get 2
			end
			DATA2:begin
				gray_addr <= plus;
				buffer[5] <= gray_data;     // get 130
			end
			DATA3:begin
				gray_addr <= minus;
				buffer[8] <= gray_data;     // get 258
				lbp_addr  <= lbp_addr + 1;
			end
			WRITE_DATA:begin
				// shift
				lbp_valid   <= (valid)? 0 : 1;
				lbp_data[0] <= (buffer[0]>=buffer[4])? 1 : 0;
				lbp_data[1] <= (buffer[1]>=buffer[4])? 1 : 0;
				lbp_data[2] <= (buffer[2]>=buffer[4])? 1 : 0;
				lbp_data[3] <= (buffer[3]>=buffer[4])? 1 : 0;
				lbp_data[4] <= (buffer[5]>=buffer[4])? 1 : 0;
				lbp_data[5] <= (buffer[6]>=buffer[4])? 1 : 0;
				lbp_data[6] <= (buffer[7]>=buffer[4])? 1 : 0;
				lbp_data[7] <= (buffer[8]>=buffer[4])? 1 : 0;

				buffer[0] <= buffer[1];
				buffer[1] <= buffer[2];
				buffer[3] <= buffer[4];
				buffer[4] <= buffer[5];
				buffer[6] <= buffer[7];
				buffer[7] <= buffer[8];
				if(fin) finish <= 1;
			end
		endcase
	end
end

always@(*)begin
	case(state_r)
			DATA1: state_w = DATA2;
			DATA2: state_w = DATA3;
			DATA3: state_w = WRITE_DATA;
			WRITE_DATA: state_w = DATA1;
	endcase
end

endmodule

## Testbench

`timescale 1ns/10ps
`define CYCLE      10          	  // Modify your clock period here
`define End_CYCLE  10000000              // Modify cycle times once your design need more cycle times!
`define PAT        "./00_TB/pattern1.dat" 
   
`define EXP        "./00_TB/golden1.dat"     


module test;

parameter N_EXP   = 16384; // 128 x 128 pixel
parameter N_PAT   = N_EXP;

reg   [7:0]   gray_mem   [0:N_PAT-1];
reg   [7:0]   exp_mem    [0:N_EXP-1];

reg [7:0] LBP_dbg;
reg [7:0] exp_dbg;
wire [7:0] lbp_data;
reg   clk = 0;
reg   rst = 0;
reg   result_compare = 0;

integer total_error = 0;
integer times = 0;
reg over = 0;
integer exp_num = 0;
wire [13:0] gray_addr;
wire [13:0] lbp_addr;
reg [7:0] gray_data;
reg gray_ready = 0;
integer i;

   TOP TOP( .clk(clk), .rst(rst), 
            .gray_addr(gray_addr), .gray_req(gray_req), .gray_ready(gray_ready), .gray_data(gray_data), 
			.lbp_addr(lbp_addr), .lbp_valid(lbp_valid), .lbp_data(lbp_data), .finish(finish));
			
   lbp_mem u_lbp_mem(.lbp_valid(lbp_valid), .lbp_data(lbp_data), .lbp_addr(lbp_addr), .clk(clk));
   

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


initial	$readmemh (`PAT, gray_mem);
initial	$readmemh (`EXP, exp_mem);

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

// initial begin
// 	$fsdbDumpfile("LBP.fsdb");
// 	$fsdbDumpvars;
// end

initial begin  // data input
   @(negedge clk)  rst = 1'b1; 
   #(`CYCLE*2);    rst = 1'b0; 
   @(negedge clk)  gray_ready = 1'b1;
    while (finish == 0) begin             
      if( gray_req ) begin
         gray_data = gray_mem[gray_addr];  
      end 
      else begin
         gray_data = 'hz;  
      end                    
      @(negedge clk); 
    end     
    gray_ready = 0; gray_data='hz;
	@(posedge clk) result_compare = 1; 
end

initial begin // result compare
	$display("-----------------------------------------------------\n");
 	$display("START!!! Simulation Start .....\n");
 	$display("-----------------------------------------------------\n");
	#(`CYCLE*3); 
	wait( finish ) ;
	@(posedge clk); @(posedge clk);
	for (i=0; i <N_PAT ; i=i+1) begin
			//@(posedge clk);  // TRY IT ! no comment this line for debugging !!
				exp_dbg = exp_mem[i]; LBP_dbg = u_lbp_mem.LBP_M[i];
				if (exp_mem[i] == u_lbp_mem.LBP_M[i]) begin
					total_error = total_error;
				end
				else begin
					//$display("pixel %d is FAIL !!", i); 
					total_error = total_error+1;
					if (total_error <= 10) $display("Output pixel %d are wrong!", i);
					if (total_error == 11) begin $display("Find the wrong pixel reached a total of more than 10 !, Please check the code .....\n");  end
				end
				if( ((i%1000) === 0) || (i == 16383))begin  
					if ( total_error === 0)
      					$display("Output pixel: 0 ~ %d are correct!\n", i);
					else
					$display("Output Pixel: 0 ~ %d are wrong ! The wrong pixel reached a total of %d or more ! \n", i, total_error);
					
  				end					
				exp_num = exp_num + 1;
	end
	over = 1;
end

integer exe_cycle;
reg ooo;
initial begin
	ooo = 0;
	exe_cycle = 0;
	repeat(3) @(posedge clk);
	while(over != 1) begin
		exe_cycle = exe_cycle + 1;
		@(negedge clk);
	end
	ooo = 1;
end


initial begin
	$display(" Cycle Period = %0f ns", `CYCLE);
end
initial  begin
 #`End_CYCLE ;
 	$display("-----------------------------------------------------\n");
 	$display("Error!!! Somethings' wrong with your code ...!\n");
 	$display("-------------------------FAIL------------------------\n");
 	$display("-----------------------------------------------------\n");
 	$finish;
end

initial begin
      @(posedge ooo);      
      if((ooo) && (exp_num!='d0)) begin
         $display("-----------------------------------------------------\n");
        if(total_error == 0) begin
            $display("------------------------------------------------");
            $display("----------------All tests PASS!-----------------");
            $display("------------total time: %0d cycles-----------------", exe_cycle);
            $display("------------------------------------------------");
        end else begin
            $display("------------------------------------------------");
            $display("-              Total Error: %0d               -", total_error);
            $display("------------------------------------------------");
        end
      end
      #(`CYCLE/2); $finish;
end


        
        

endmodule


module lbp_mem (lbp_valid, lbp_data, lbp_addr, clk);
input		lbp_valid;
input	[13:0] 	lbp_addr;
input	[7:0]	lbp_data;
input		clk;

reg [7:0] LBP_M [0:16383];
integer i;

initial begin
	for (i=0; i<=16383; i=i+1) LBP_M[i] = 0;
end

always@(negedge clk) 
	if (lbp_valid) LBP_M[ lbp_addr ] <= lbp_data;

endmodule