# Human Version — Distance Transform (DT): Complete Design Specification

## 1) Problem Description

Design a hardware **Distance Transform (DT)** engine that reads a **binary 128×128 image** from a host **input ROM (`sti_ROM`)**, computes an **8-distance (chessboard) transform**, and writes the resulting **8-bit grayscale 128×128 image** to a host **output RAM (`res_RAM`)**. When the full frame is written, assert `done=1`. The object pixels are denoted by **1**, background by **0**; test data guarantee the object region does **not** touch the outermost border ring.

---

## 2) Top-Level Module & I/O

**Top module name:** `TOP`  (Verilog: `module TOP (...);`)

**Clocking & rst**

* `clk`: rising-edge synchronous.
* `rst`: **active-high, Synchronous**. While `rst=1`, clear state and do not produce valid writes/`done`. The testbench holds `rst=1` for \~2 cycles then deasserts to `0`.

**Ports (fixed names, widths, semantics)**

| Signal                                           | Dir | Width | Meaning                                                                                |
| ------------------------------------------------ | --: | ----: | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `clk`                                            |   I |     1 | System clock (posedge).                                                                                |
| `rst`                                          |   I |     1 | **Active-high sync rst**                                                                                |
| `sti_rd`                                         |   O |     1 | `sti_ROM` **read enable**; when High at **negedge**, data appears immediately on `sti_di`.                                                   |
| `sti_addr`                                       |   O |    10 | `sti_ROM` **address** (0..1023). **At most one** address read per cycle. Keep stable across the servicing **negedge**.                       |
| `sti_di`                                         |   I |    16 | `sti_ROM` **read data**: 16 packed binary pixels.                                                                                |
| `res_wr`                                         |   O |     1 | `res_RAM` **write enable**; when High at **posedge**, write `res_do` to `res_addr`.                                                          |
| `res_rd`                                         |   O |     1 | `res_RAM` **read enable**; when High at **negedge**, data appears immediately on `res_di`.                                                   |
| `res_addr`                                       |   O |    14 | `res_RAM` address (0..16383). **At most one** read and **at most one** write address serviced per cycle (read at negedge, write at posedge). |
| `res_do`                                         |   O |     8 | `res_RAM` **write data** (grayscale).                                                                                |
| `res_di`                                         |   I |     8 | `res_RAM` **read data**.                                                                                |
| `done`                                           |   O |     1 | Assert High **after** all result pixels are written; 1 indicates completion; host then checks results and ends simulation.                   |
| (Ports and timing semantics per problem brief.)  |     |       |                                                                                |

---

## 3) External Memories: Mapping & Timing

### 3.1 Input binary image ROM — `sti_ROM` (read-only)

* **Image size:** 128×128 = 16,384 pixels, **1 bit per pixel**.
* **ROM geometry:** width **16 bits**, depth **1024** addresses.
* **Address-to-pixel mapping (row-major):** address `a` holds **pixels \[16a .. 16a+15]** corresponding to a **left-to-right** run in the raster order, then top-to-bottom across rows. Treat the 16-bit word as a 16-pixel pack. (The testbench uses this contiguous, row-major packing.)
* **Timing:** On **each negative clock edge**, if `sti_rd=1`, `sti_di` **immediately** drives the data of the address on `sti_addr` (no additional latency). If `sti_rd=0`, no action. **Issue at most one ROM address per cycle**; keep `sti_addr` stable through the servicing **negedge**.

### 3.2 Output grayscale image RAM — `res_RAM` (read/write)

* **Result size:** 128×128 = 16,384 pixels, **8 bits per pixel**.
* **Addressing (row-major):** write pixels **left-to-right, top-to-bottom** to addresses **0..16383**. You may also re-use `res_RAM` as scratch during computation.
* **Timing:**

  * **READ:** If `res_rd=1` on a **negedge**, `res_di` **immediately** reflects `res_addr` (no extra latency).
  * **WRITE:** If `res_wr=1` on a **posedge**, `res_do` is written to `res_addr`.
  * In a given cycle you may perform **one read (at negedge)** and **one write (at posedge)**.

---

## 4) Algorithm — 8-Distance (Chessboard) Transform

We compute, for each **object pixel** (input bit = 1), the **minimum chessboard distance** to any **background pixel** (input bit = 0), using a **forward pass** then a **backward pass** over the raster. Background pixels remain 0. (The object region never touches the outermost image border.)

**Coordinate & neighborhood notation**
At location `(x,y)`, define neighbors:

* **Forward window:** `NW, N, NE, W`
* **Backward window:** `E, SE, S, SW`
  (These are the four preceding neighbors in the forward scan and the four succeeding neighbors in the backward scan, respectively.)

**Initialization**
Set each pixel to **1** if object, **0** if background (i.e., use the binary input as initial scalar field).

**Forward pass (scan left→right, top→bottom)**
For every **object** pixel `p(x,y)` (the red “center” in the figure), compute
`x = min( NW, N, NE, W )`, then **update** `p(x,y) ← x + 1`.
(Background pixels keep value 0.)

**Backward pass (scan right→left, bottom→top)**
For every **object** pixel `p(x,y)`, compute
`z = min( E, SE, S, SW )`, then `y = min( p(x,y), z + 1 )`, and **update** `p(x,y) ← y`.
After this pass, `p` holds the chessboard distance to the nearest background pixel.

---

## 5) System-Level Operation Sequence & Control

1. **rst** (`rst=1` for \~2 cycles): clear all internal state; no reads/writes; `done=0`.
2. **ROM read / compute:** after `rst` deasserts (`rst=0`), drive `sti_rd/sti_addr` to fetch binary data packs from `sti_ROM` and perform the DT algorithm. You **may** use `res_RAM` as temporary storage via `res_rd/res_wr` during forward/backward passes.
3. **Write-out:** write the **final 8-bit result image** to `res_RAM` **row-major** via `res_wr/res_addr/res_do` (write on posedges).
4. **Finish:** after all 16,384 result pixels are written, **assert `done=1`**; the host immediately starts result checking and ends the simulation on completion.

---

## 6) Constraints & Notes

* **Do not** modify testbench or memory modules; observe the **exact** port names, widths, and edge-sensitive protocols above.
* **Throughput/latency** are not constrained by the spec beyond the memory protocols; any correct micro-architecture is acceptable (single/dual pass buffering, streaming or tiled, etc.).
* **Boundary safety**: the input guarantees the object region does **not** touch the outermost border, simplifying neighborhood access during forward/backward scans.

---

module TOP(
	input 			clk,
	input			rst,
	output	reg		done ,
	output			sti_rd ,
	output	reg [9:0]	sti_addr ,
	input	[15:0]	sti_di,
	output			res_wr ,
	output			res_rd ,
	output	reg [13:0]	res_addr ,
	output	reg [7:0]	res_do,
	input		[7:0]	res_di
);

integer i;

reg [15:0] idata_buf;
reg [3:0] dup_cnt;
reg [2:0] cnt_calc;
reg flag_fb;

reg [6:0] y, x ;

reg [7:0] mini;
reg [7:0] res_input_buf;
//================================================================
//  FSM
//================================================================
parameter IDLE = 0, INPUT = 1, COMP = 2, OUTPUT = 3, READMEM = 4, DELAY = 5, COPY = 6, FINISH = 7;

reg [2:0] state_r, state_w;

always @( posedge clk or negedge 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 = COPY;
		COPY: begin
			if( dup_cnt == 0 ) begin
				if( sti_addr == 1023 ) state_w = READMEM;
				else state_w = INPUT;
			end
			else state_w = COPY;
		end
		READMEM: begin
			if ( res_addr == 0 && flag_fb ) state_w = FINISH;
			else if( res_addr == 16383 && !flag_fb ) state_w = DELAY;
			else state_w = ( res_di != 0 )? COMP : READMEM;
		end
		COMP: state_w = ( cnt_calc == 4 )? OUTPUT : COMP ;
		OUTPUT: state_w = READMEM;
		DELAY: state_w = READMEM;
		FINISH: state_w = FINISH;
		default: state_w = IDLE;
	endcase
end

//================================================================
//  INPUT
//================================================================
assign sti_rd = ( state_r == INPUT );

always @(posedge clk or negedge rst ) begin
	if( !rst ) idata_buf <= 0;
	else if( state_r == INPUT ) begin
		for( i=0; i<16; i=i+1 ) idata_buf[15-i] <= sti_di[i];
	end
end

always @(posedge clk or negedge rst) begin
	if(!rst) res_input_buf <= 0;
	else if(state_r == READMEM) res_input_buf <= res_di;
end
//================================================================
//  OUTPUT
//================================================================
always @( posedge clk or negedge rst ) begin
	if( !rst ) sti_addr <= 0;
	else if( state_r == COPY && dup_cnt == 0 ) sti_addr <= sti_addr + 1;
end

wire [3:0] addr_tmp;
assign addr_tmp  = dup_cnt-1;

always @( * ) begin
	case( state_r )
		COPY:  res_do = idata_buf[addr_tmp];
		OUTPUT: begin
			if( flag_fb ) begin
					if( res_input_buf > mini+1 ) res_do = mini + 1;
					else res_do = res_input_buf;
			end
			else	res_do = mini + 1;
		end
		default: res_do = 0;
	endcase
end

always @(posedge clk or negedge rst ) begin
	if( !rst ) dup_cnt <= 0;
	else if( state_w == COPY ) dup_cnt <= dup_cnt + 1;
	else dup_cnt <= 0;
end

reg [13:0] addr_forw[3:0];

always @( * ) begin
	if( !flag_fb ) begin
		addr_forw[0] = {y-7'd1, x-7'd1} ;
		addr_forw[1] = {y-7'd1, x     } ;
		addr_forw[2] = {y-7'd1, x+7'd1} ;
		addr_forw[3] = {y     , x-7'd1} ;
	end
	else begin
		addr_forw[0] = {y     , x+7'd1} ;
		addr_forw[1] = {y+7'd1, x-7'd1} ;
		addr_forw[2] = {y+7'd1, x     } ;
		addr_forw[3] = {y+7'd1, x+7'd1} ;
	end
end

always @(posedge clk or negedge rst ) begin
	if( !rst ) 							res_addr <= 0;
	else if( state_w == COPY ) 	res_addr <= {sti_addr,dup_cnt};
	else if( state_w == READMEM ) 	res_addr <= {y,x};
	else if( state_w == COMP ) 		res_addr <= ( flag_fb )? addr_forw[cnt_calc] + 1:addr_forw[cnt_calc] - 1;
	else if( state_w == OUTPUT ) 	res_addr <= ( flag_fb )? {y,x} + 1:{y,x} - 1;
	else if( state_w == DELAY ) 		res_addr <= 16383;
end

always @( posedge clk or negedge rst ) begin
	if( !rst ) done <= 0;
	else if( state_r == FINISH ) done <= 1;
end

assign res_wr = ( state_r == COPY || state_r == OUTPUT );
assign res_rd = ( state_r == READMEM  || state_r == COMP   );

assign fw_finish = flag_fb;
//================================================================
//  x && y
//================================================================
always @(posedge clk or negedge rst) begin
	if(!rst) begin
		x <= 0; y <= 0;
	end
	else if( state_w == READMEM ) begin
		if( flag_fb ) begin
			x <= x - 1; y<= (x == 0)? y-1:y;
		end
		else begin
			x <= x + 1; y <= ( x == 127 )? y+1 : y;
		end
	end
	else if( state_w == DELAY ) begin
		x <= 127; y <= 127;
	end
end

always @( posedge clk or negedge rst ) begin
	if(!rst) cnt_calc <= 0;
	else if( state_w == COMP ) cnt_calc <= cnt_calc + 1;
	else cnt_calc <= 0;
end

//================================================================
//  flag
//================================================================
always @(posedge clk or negedge rst ) begin
	if(!rst) flag_fb <= 0;
	else if( state_r == READMEM ) begin
		if( res_addr == 16383 ) flag_fb <= 1;
	end
end

//================================================================
//  mini
//================================================================
always @(posedge clk or negedge rst ) begin
	if(!rst) mini <= 0;
	else if( state_r == COMP ) begin
		if( cnt_calc == 1 ) mini <= res_di;
		else begin
			if( res_di < mini ) mini <= res_di;
		end
	end
end

endmodule

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

`define TB1
`ifdef TB1
	`define PAT          "./00_TB/dat/Geometry_sti.dat"
	`define FWEXP        "./00_TB/dat/Geometry_fwexp.dat"
	`define BCEXP        "./00_TB/dat/Geometry_bcexp.dat"
`elsif TB2
	`define PAT          "./00_TB/dat/ICC17_sti.dat"
	`define FWEXP        "./00_TB/dat/ICC17_fwexp.dat"
	`define BCEXP        "./00_TB/dat/ICC17_bcexp.dat"
`endif

module test;

parameter N_PAT = 16383;

reg   [7:0]   exp_fwpass    [0:N_PAT];
initial	$readmemh (`FWEXP, exp_fwpass);

reg   [7:0]   exp_bcpass    [0:N_PAT];
initial	$readmemh (`BCEXP, exp_bcpass);

wire		fwpass_finish, done;
wire		sti_rd;
wire	[9:0]	sti_addr;
wire		res_wr;
wire		res_rd;
wire	[13:0]	res_addr;
wire	[7:0]	res_do;
wire	[15:0]	sti_di;
wire	[7:0]	res_di;

integer		i, fw_err, bc_err;

reg		fwpass_chk, bcpass_chk;
reg	[7:0]	exp_pat, rel_pat;

reg	[7:0]	fwexp_pat, bcexp_pat;

reg		clk = 0;
reg		rst;

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

TOP TOP(	.clk( clk ), .rst( rst ),
			.done( done ),
			.sti_rd( sti_rd ),
			.sti_addr( sti_addr ),
			.sti_di( sti_di ),
			.res_wr( res_wr ),
			.res_rd( res_rd ),
			.res_addr( res_addr ),
			.res_do( res_do ),
			.res_di( res_di ) );

sti_ROM  u_sti_ROM(.sti_rd(sti_rd), .sti_data(sti_di), .sti_addr(sti_addr), .clk(clk), .rst(rst));
res_RAM  u_res_RAM(.res_rd(res_rd), .res_wr(res_wr), .res_addr(res_addr), .res_datain(res_do), .res_dataout(res_di), .clk(clk));

always begin #(`CYCLE/2) clk = ~clk; end
initial begin
    $display(" Cycle Period = %0f ns", `CYCLE);
end
initial begin
	`ifdef FSDB
		$fsdbDumpfile("top.fsdb");
		$fsdbDumpvars(0, test);
	`elsif VCD
		$dumpfile("top.vcd");
		$dumpvars;
	`endif
end

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

initial begin
	#(`End_CYCLE);
	$display("-----------------------------------------------------\n");
	$display("Error!!! There is something wrong with your code ...!\n");
 	$display("------The test result is .....FAIL ------------------\n");
 	$display("-----------------------------------------------------\n");
 	$finish;
end

initial begin // FW-PASS result compare
fwpass_chk = 0;
	#(`CYCLE*3);
	wait( fwpass_finish ) ;
	fwpass_chk = 1;
	fw_err = 0;
	for (i=0; i <N_PAT ; i=i+1) begin
				exp_pat = exp_fwpass[i];
				rel_pat = u_res_RAM.res_M[i];
				if (exp_pat == rel_pat) begin
					fw_err = fw_err;
				end
				else begin
					fw_err = fw_err+1;
					if (fw_err <= 30) $display("FWPASS : Output pixel %d are wrong! the real output is %h, but expected result is %h", i, rel_pat, exp_pat);
					if (fw_err == 31) begin $display("FWPASS : Find the wrong pixel reached a total of more than 30 !, Please check the code .....\n");  end
				end
				if( ((i%1000) === 0) || (i == 16383))begin
					if ( fw_err === 0)
      					$display("FWPASS : Output pixel: 0 ~ %d are correct!\n", i);
					else
					$display("FWPASS : Output Pixel: 0 ~ %d are wrong ! The wrong pixel reached a total of %d or more ! \n", i, fw_err);

  				end
	end
end

initial begin // BC-PASS result compare
bcpass_chk = 0;
	#(`CYCLE*3);
	wait( done ) ;
	bcpass_chk = 1;
	bc_err = 0;
	for (i=0; i <N_PAT ; i=i+1) begin
				exp_pat = exp_bcpass[i];
				rel_pat = u_res_RAM.res_M[i];
				if (exp_pat == rel_pat) begin
				        bc_err = bc_err;
				end
				else begin
					bc_err = bc_err+1;
					if (bc_err <= 16384) $display(" Output pixel %d are wrong!the real output is %h, but expected result is %h", i, rel_pat, exp_pat);
					if (bc_err == 31) begin $display(" Find the wrong pixel reached a total of more than 30 !, Please check the code .....\n");  end
				end
				if( ((i%1000) === 0) || (i == 16383))begin
					if ( bc_err === 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, bc_err);

  				end
	end
end

reg out_end;
integer exe_cycle;
integer total_error;
initial begin

	   out_end = 0;
      @(posedge bcpass_chk)  #1;
      if( bc_err == 0 ) begin
            $display("-------------------------------------------------------------\n");
	    $display("Congratulations!!! All data have been generated successfully!\n");
            $display("---------- The test result is ..... PASS --------------------\n");
	    $display("                                                     \n");
         end
	else if ( fw_err == 0) begin
	    $display("Forward-Pass PASS! but Back-Pass FAIL, There are %d errors at back-pass run\n", bc_err);
	    $display("--------------- The test result is .....FAIL ----------------\n");
         end
       else begin
	    $display("FAIL! There are %d errors at forward-pass run!\n", fw_err);
	    $display("FAIL! There are %d errors at functional simulation !\n", bc_err);
	    $display("---------- The test result is .....FAIL -------------\n");
         end
	 $display("-----------------------------------------------------\n");
    //   #(`CYCLE/3); $finish;
	out_end = 1;
end

initial begin
	total_error = 0;
   repeat(5)@(negedge clk);
	exe_cycle = 0;
	while(!out_end) begin
		exe_cycle = exe_cycle + 1;
		@(negedge clk);
	end
	total_error = bc_err;
	$display("-----------------------------------------------------------------");
	$display("------------total time: %0d cycles-----------------", exe_cycle);
	$display("-----------------------------------------------------------------");

	if(total_error == 0) begin
		$display("------------------------------------------------");
		$display("----------------All tests PASS!-----------------");

		$display("------------------------------------------------");
	end else begin
		$display("------------------------------------------------");
		$display("-              Total Error: %0d               -", total_error);
		$display("------------------------------------------------");
	end

	$finish;
end

initial begin
	@(posedge fwpass_chk) #1;
	 if ( fw_err == 0) begin
            $display("                                                     \n");
	    $display("Forward-Pass test PASS! ");
	    $display("                                                     \n");
         end
	 else begin
	    $display("                                                     \n");
	    $display("Forward-Pass FAIL! There are %d errors at forward-pass run!\n", fw_err);
	    $display("---------- The test result is .....FAIL -------------\n");
	    $display("                                                     \n");
	 end
end

endmodule

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
module sti_ROM (sti_rd, sti_data, sti_addr, clk, rst);
input		sti_rd;
input	[9:0] 	sti_addr;
output	[15:0]	sti_data;
input		clk, rst;

reg [15:0] sti_M [0:1023];
integer i;

reg	[15:0]	sti_data;

initial begin
	@ (negedge rst) $readmemh (`PAT , sti_M);
	end

always@(negedge clk)
	if (sti_rd) sti_data <= sti_M[sti_addr];

endmodule

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
module res_RAM (res_rd, res_wr, res_addr, res_datain, res_dataout, clk);
input		res_rd, res_wr;
input	[13:0] 	res_addr;
input	[7:0]	res_datain;
output	[7:0]	res_dataout;
input		clk;

reg [7:0] res_M [0:16383];

integer i;

initial for(i=0;i<=16383;i=i+1) res_M[i] = 8'h0;

reg [7:0] res_dataout;
always@(negedge clk)   // read data at negedge clock
	if (res_rd) res_dataout <= res_M[res_addr];

always@(posedge clk)   // write data at posedge clock
	if (res_wr) res_M[res_addr] <= res_datain;

endmodule