dakies/OSS_Verilog huggingface dataset:
module camera(
input               clk,                                  // clock
input               camera_en,                            // camera enable
output reg          data_valid,                           // data valid
output reg [7:0]    data_out);                            // output data bus


reg [7:0] DATA [0:74];                                    // Camera Data
integer ptr;

initial
begin
 ptr <= 0;
 DATA[0] <= 8'hBC;
 DATA[1] <= 8'h27;
 DATA[2] <= 8'h81;
 DATA[3] <= 8'hFF;
 DATA[4] <= 8'hCE;
 DATA[5] <= 8'h1F;
 DATA[6] <= 8'hE0;
 DATA[7] <= 8'hA9;
 DATA[8] <= 8'h38;
 DATA[9] <= 8'h2B;
 DATA[10] <= 8'hD4;
 DATA[11] <= 8'h11;
 DATA[12] <= 8'h10;
 DATA[13] <= 8'h0F;
 DATA[14] <= 8'h6E;
 DATA[15] <= 8'hBF;
 DATA[16] <= 8'h50;
 DATA[17] <= 8'hA3;
 DATA[18] <= 8'h45;
 DATA[19] <= 8'h2C;
 DATA[20] <= 8'h44;
 DATA[21] <= 8'h01;
 DATA[22] <= 8'h21;
 DATA[23] <= 8'hDF;
 DATA[24] <= 8'h1E;
 DATA[25] <= 8'h7F;
 DATA[26] <= 8'h80;
 DATA[27] <= 8'h39;
 DATA[28] <= 8'h48;
 DATA[29] <= 8'hCB;
 DATA[30] <= 8'hC4;
 DATA[31] <= 8'hCC;
 DATA[32] <= 8'h8F;
 DATA[33] <= 8'hF2;
 DATA[34] <= 8'hC4;
 DATA[35] <= 8'h10;
 DATA[36] <= 8'hE1;
 DATA[37] <= 8'hA0;
 DATA[38] <= 8'h30;
 DATA[39] <= 8'h24;
 DATA[40] <= 8'hD6;
 DATA[41] <= 8'h16;
 DATA[42] <= 8'h91;
 DATA[43] <= 8'hF9;
 DATA[44] <= 8'h43;
 DATA[45] <= 8'h13;
 DATA[46] <= 8'hE0;
 DATA[47] <= 8'hA2;
 DATA[48] <= 8'h38;
 DATA[49] <= 8'h2F;
 DATA[50] <= 8'hD4;
 DATA[51] <= 8'h15;
 DATA[52] <= 8'h8C;
 DATA[53] <= 8'hFF;
 DATA[54] <= 8'hC1;
 DATA[55] <= 8'h1F;
 DATA[56] <= 8'hE5;
 DATA[57] <= 8'hA9;
 DATA[58] <= 8'h3B;
 DATA[59] <= 8'h2B;
 DATA[60] <= 8'hDD;
 DATA[61] <= 8'h11;
 DATA[62] <= 8'h8E;
 DATA[63] <= 8'hFF;
 DATA[64] <= 8'hCA;
 DATA[65] <= 8'h1F;
 DATA[66] <= 8'hEE;
 DATA[67] <= 8'hA9;
 DATA[68] <= 8'h37;
 DATA[69] <= 8'h2B;
 DATA[70] <= 8'hE4;
 DATA[71] <= 8'h11;
 DATA[72] <= 8'hE1;
 DATA[73] <= 8'hFF;
 DATA[74] <= 8'hDE;
end

always @(posedge clk)
begin
 if(camera_en)
 begin
  data_valid <= 1'b1;
  data_out <= DATA[ptr];
  ptr <= ptr + 1;
 end
 else
 begin
  data_valid <= 1'b0;
  ptr <= 0;
  data_out <= 8'hzz;
 end
 if(ptr == 74)
  ptr <= 0;
end


endmodule
module sobel_filter(
input         clk,
input         rst_n,
input         data_valid,
input [7:0]   Din,
output        output_valid,
output [15:0] Dout,
output        CHL_X,
output        CHL_Y,
output        done);

reg [7:0] storage[0: (N*M)-1];
reg [7:0] sobel_kernal[0:8];

reg [15:0] sobel_x;
reg [15:0] sobel_y;
reg [15:0] result;
reg channel_x;
reg channel_y;

reg [2:0] PS, NS;

integer count, i, j, k, p, q, o;
parameter IDLE = 3'b000, STORE = 3'b001, FIX = 3'b010, CONVOLUTE = 3'b011;

parameter N = 450, M = 450;

//sequential logic
always @(posedge clk or negedge rst_n)
begin
 if (~rst_n)
  PS <= IDLE;
 else PS <= NS;
end

always @(posedge clk)
begin
 if(data_valid)
 begin
  storage[i] <= Din;  
  i <= (i == N*M-1) ? 0 : i + 1;
 end
 else i <= 0;
end

always @(Din,data_valid,i,count,k,PS)
begin
 case (PS)
 IDLE: begin
        sobel_x = 16'h0000;
        sobel_y = 16'h0000;
        j = 0;
        k = 0;
        o = 0;
        count = 0;
        if(data_valid)
         NS = STORE;
        else NS = IDLE;
       end
 STORE: begin
        NS = (i == N*M-1) ? FIX : STORE;
        end
 FIX: begin
       o = 0;
       for(p = 0; p < 3; p = p+1)
       begin
        for(q = 0; q < 3; q = q+1)
        begin
         sobel_kernal[o] = storage[j + (p*M) + q];
         o = o + 1;
        end
       end

       NS = CONVOLUTE;
      end
 CONVOLUTE: begin
              sobel_x = (sobel_kernal[2] + (2*sobel_kernal[5]) + sobel_kernal[8]) - (sobel_kernal[0] + (2*sobel_kernal[3]) + sobel_kernal[6]);
              sobel_y = (sobel_kernal[0] + (2*sobel_kernal[1]) + sobel_kernal[2]) - (sobel_kernal[6] + (2*sobel_kernal[7]) + sobel_kernal[8]);
              channel_x = ((sobel_kernal[0] + (2*sobel_kernal[3]) + sobel_kernal[6]) > (sobel_kernal[2] + (2*sobel_kernal[5]) + sobel_kernal[8])) ? 1'b0 : 1'b1;
              channel_y = ((sobel_kernal[6] + (2*sobel_kernal[7]) + sobel_kernal[8]) > (sobel_kernal[0] + (2*sobel_kernal[1]) + sobel_kernal[2])) ? 1'b0 : 1'b1;
              result = ((sobel_x * sobel_x) + (sobel_y * sobel_y))**0.5;

              k = (k == M-3) ? 0 : k + 1;
              j = (k == 0) ? j + 3 : j + 1;

              count = count + 1;
              NS = (count==(N-2)*(M-2)) ? IDLE : FIX;
            end
 endcase
end

assign output_valid = (PS==CONVOLUTE) ? 1'b1 : 1'b0;
assign Dout = (PS==CONVOLUTE) ? result : 16'hzzzz;
assign CHL_X = (PS==CONVOLUTE) ? channel_x : 1'bz;
assign CHL_Y = (PS==CONVOLUTE) ? channel_y : 1'bz;
assign done = (count == (N-2)*(M-2)) ? 1'b1 : 1'b0;
endmodule
module Controller(
input             clk,                      //clock
input             rst_n,                    //external asynchronous active low reset
input             RWM_1_done,               //status signal from the RWM_1 module
input             RWM_2_done,               //status signal from the RWM_2 module
input             GS_done,                  //status signal from the Grayscaler module
input             start,                    //external start command from user
output            RWM_1_enable,             //status signal to the RWM_1 module
output            rw_1,                     //status signal to the RWM_1 module
output            RWM_2_enable,             //status signal to the RWM_2 module
output            rw_2,                     //status signal to the RWM_2 module
output            camera_enable,            //status signal to the camera
output            GS_enable                 //status signal to the Grayscaler module
);

parameter [2:0] IDLE = 3'b000, CAMERA_READ = 3'b001, GRAYSCALE = 3'b010, FILTER = 3'b011;
reg [2:0] CS, NS;

//Sequential logic
always @(posedge clk or negedge rst_n)
begin
 if (~rst_n)
  CS <= IDLE;
 else CS <= NS;
end

//Combinatorial logic
always @(start, GS_done, RWM_1_done, RWM_2_done)
begin
 case (CS)
 IDLE:
 begin
  if (start == 1'b1)
   NS = CAMERA_READ;
  else NS = IDLE;
 end
 CAMERA_READ:
 begin
  if (RWM_1_done == 1'b1)
   NS = GRAYSCALE;
  else NS = CAMERA_READ;
 end
 GRAYSCALE:
 begin
  if (RWM_1_done == 1'b1)
   NS = FILTER;
  else NS = GRAYSCALE;
 end
 FILTER:
 begin
  if ((GS_done == 1'b1) || RWM_2_done == 1'b0)
   NS = FILTER;
  else NS = IDLE;
 end
 default: NS = IDLE;
 endcase
end

assign camera_enable = (CS == CAMERA_READ) ? 1'b1 : 1'b0;
assign RWM_1_enable = ((CS == CAMERA_READ) || (CS == GRAYSCALE)) ? 1'b1 : 1'b0;
assign rw_1 = (CS == CAMERA_READ) ? 1'b1 :((CS == GRAYSCALE) ? 1'b0 : 1'bz);
assign RWM_2_enable = ((CS == GRAYSCALE) || (CS == FILTER)) ? 1'b1 : 1'b0;
assign rw_2 = (CS == GRAYSCALE) ? 1'b1 : ((CS == FILTER) ? 1'b0 : 1'bz);
assign GS_enable = (CS == GRAYSCALE) ? 1'b1 : 1'b0;

endmodule
module stitcher(
input         clk,
input         rst_n,
input         left_data_valid,
input [7:0]   left_data,
input         right_data_valid,
input [7:0]   right_data,
input         keypoint_valid,
input [31:0]  left_keypoint,
input [31:0]  right_keypoint,
output        data_valid,
output [7:0]  data);

reg [7:0] L_DATA[0:(3*N*M - 1)];
reg [7:0] R_DATA[0:(3*N*M - 1)];
reg [7:0] IMAGE[0:(6*N*M - 1)];
reg [7:0] pixel_value;
reg [7:0] out;
reg [2:0] PS, NS;

parameter IDLE = 3'b000, STORE = 3'b001, LOAD = 3'b010, COMPUTE = 3'b011, BLEND = 3'b100, DISPLAY = 3'b101;
parameter N = 450, M = 450;

integer i, j, k, p, q, o, row1, row2, col1, col2, ref_keypoint1, ref_keypoint2;

always @(posedge clk or negedge rst_n)
begin
 if (~rst_n)
  PS <= IDLE;
 else
  PS <= NS;
end

always @(posedge clk)
begin
 if (left_data_valid)
 begin
  L_DATA[i] <= left_data;
  i <= i + 1;
 end
 else
  i <= i;
end

always @(posedge clk)
begin
 if (right_data_valid)
 begin
  R_DATA[i] <= right_data;
  j <= j + 1;
 end
 else
  j <= j;
end

always @(left_data_valid, right_data_valid,left_data, right_data, keypoint_valid, PS)
begin
 case (PS)
 IDLE: begin
        i = 0;
        j = 0;
        k = 0;
        p = 0;
        q = 0;
        o = 0;
        row1 = 0;
        row2 = 0;
        col1 = 0;
        col2 = 0;
        ref_keypoint1 = 0;
        ref_keypoint2 = 0;
        pixel_value = 0;
        for(p = 0; p < 6*N*M; p = p+1)
        begin
         IMAGE[p] = 0;
        end
        if (left_data_valid || right_data_valid)
         NS = STORE;
        else
         NS = IDLE;
       end
 STORE: begin
         NS = (i == 3*N*M - 1 && j == 3*N*M - 1) ? LOAD : STORE;
        end
 LOAD: begin
        ref_keypoint1 = (keypoint_valid == 1'b1) ? left_keypoint : ref_keypoint1;
        ref_keypoint2 = (keypoint_valid == 1'b1) ? right_keypoint : ref_keypoint2;
        NS = (keypoint_valid == 1'b1) ? COMPUTE : LOAD;
       end
 COMPUTE: begin
           row1 = ref_keypoint1/M;
           row2 = ref_keypoint2/M;
           col1 = 3*(ref_keypoint1 - (row1*M));
           col2 = 3*(ref_keypoint2 - (row2*M));
           NS = BLEND;
          end
 BLEND: begin
         pixel_value = (q <= col1) ? L_DATA[q + (o*3*M)] : R_DATA[col2 + (q - col1) + (o*3*M)];
         IMAGE[q + (6*o*M)] = pixel_value;
         o = (q == (((3*M)-col2)+col1)-1) ? o + 1 : o;
         q = (q == (((3*M)-col2)+col1)-1) ? 0 : q + 1;
         NS = DISPLAY;
        end
 DISPLAY: begin
           out = IMAGE[k];
           k = k + 1;
           NS = (k == 6*N*M) ? IDLE : BLEND;
          end
 endcase
end
assign data_valid = (PS == DISPLAY) ? 1'b1 : 1'b0;
assign data = (PS == DISPLAY) ? out : 8'hzz;
endmodule
module gaussian();
reg [7:0] s;
reg [7:0] size;
reg [7:0] G;

initial
begin
 size = 4*s + 1;
 G = 1/(2*3.1415);
 $display("%f", G);
end
endmodule
module slave_1_tb();

reg clk, rst_n, slave_en;
wire slave_done;
wire [3:0] slave_out;

slave_1 obey (clk, rst_n, slave_en, slave_done, slave_out);

initial 
begin
 $dumpfile("slave_1_tb.vcd");
 $dumpvars(0, slave_1_tb);

 rst_n = 0;
 slave_en = 0;
 #10;
 rst_n = 1;
 slave_en = 1;
 #40;
 slave_en = 0;
 #200;
 $finish;
end

always
begin
 clk = 1'b1;
 #5;
 clk = 1'b0;
 #5;
end
endmodule
module slave_1(
 input              clk,
 input              rst_n,
 input              slave_en,
 output             slave_done,
 output reg [3:0]   slave_out
);
always @(posedge clk or negedge rst_n)
begin
 if (~rst_n)
  slave_out <= 0;
 else if (slave_en)
  slave_out <= slave_out + 1;
 else slave_out <= 0;
end
assign slave_done = (slave_out == 4) ? 1'b1 : 1'b0;
endmodule
module controller(
input clk,                 //clock
input rst_n,               //external reset
input done_1,              //
input done_2,
input start,               //external start command
output trigger_1,          //
output trigger_2
);

parameter [1:0] IDLE = 2'b00, SLAVE_1 = 2'b01, SLAVE_2 = 2'b10;
reg [1:0] CS, NS;

always @(posedge clk or negedge rst_n)
begin
 if (~rst_n)
  CS <= IDLE;
 else CS <= NS;
end

always @(start, done_1, done_2)
begin
 case(CS)
 IDLE: begin
        if (start == 1'b1)
         NS = SLAVE_1;
        else NS = IDLE;
       end
 SLAVE_1: begin
               if (done_1 == 1'b1)
                NS = SLAVE_2;
               else NS = SLAVE_1;
              end
 SLAVE_2: begin
               if (done_2 == 1'b1)
                NS = IDLE;
               else NS = SLAVE_2;
             end
 default: NS = IDLE;
 endcase
end

assign trigger_1 = (CS == SLAVE_1) ? 1'b1 : 1'b0; 
assign trigger_2 = (CS == SLAVE_2) ? 1'b1 : 1'b0; 

endmodule
module controller_tb();

reg clk, rst_n, start;
wire done_1, done_2, trigger_1, trigger_2;
wire [3:0] slave_out_1, slave_out_2;

slave_1 one (clk, rst_n, trigger_1, done_1, slave_out_1);
slave_1 two (clk, rst_n, trigger_2, done_2, slave_out_2);
controller control (clk, rst_n, done_1, done_2, start, trigger_1, trigger_2);

initial
begin
 $dumpfile("controller_tb.vcd");
 $dumpvars(0, controller_tb);

 rst_n = 0;
 start = 0;
 #10;
 rst_n = 1;
 start = 1;
 #200;
 $finish;
end

always
begin
 clk = 1'b1;
 #5;
 clk = 1'b0;
 #5;
end
endmodule
module msg_expansion
	(
		clk_in,
		reset_n_in,
		message_in,
		start_in,
		index_j_in,
		word_p_out,
		word_out,
		msg_exp_finished_out
	);
	
`define	IDLE	2'b00
`define	WORKING	2'b01

input				clk_in;
input				reset_n_in;	
input	[511 : 0]	message_in;			
input				start_in;		
input	[5:0]		index_j_in;		

output	reg[31 : 0]	word_p_out;			
output	reg[31 : 0]	word_out;			
output				msg_exp_finished_out;

reg					working_en;
reg					msg_exp_finished_out;
reg		[1:0]		current_state,
					next_state;
reg		[31 : 0]	w0;
reg		[31 : 0]	w1;
reg		[31 : 0]	w2;
reg		[31 : 0]	w3;
reg		[31 : 0]	w4;
reg		[31 : 0]	w5;
reg		[31 : 0]	w6;
reg		[31 : 0]	w7;
reg		[31 : 0]	w8;
reg		[31 : 0]	w9;
reg		[31 : 0]	w10;
reg		[31 : 0]	w11;
reg		[31 : 0]	w12;
reg		[31 : 0]	w13;
reg		[31 : 0]	w14;
reg		[31 : 0]	w15;

wire	[31 : 0]	tmp_shift_15;	
wire	[31 : 0]	tmp_shift_7;	
wire	[31 : 0]	data_for_p1;
wire	[31 : 0]	data_after_p1;
wire	[31 : 0]	word_update;

assign	tmp_shift_15	=	{w13[31-15:0], w13[31:31-15+1]};
assign	tmp_shift_7		=	{w3[31-7:0],   w3[31:31-7+1]};
assign	data_for_p1		=	w0 ^ w7 ^ tmp_shift_15;
assign	data_after_p1	=	data_for_p1 ^ {data_for_p1[31-15:0], data_for_p1[31:31-15+1]} ^ {data_for_p1[31-23:0], data_for_p1[31:31-23+1]};
assign	word_update		=	data_after_p1 ^ tmp_shift_7 ^ w10;


always@(posedge clk_in)
	if(!reset_n_in)
		{w0, w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15}	<=	512'd0;
	else if(start_in)
		{w0, w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15}	<=	message_in;
	else if(working_en)
		{w0, w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15}	<=	{w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, word_update};
	else
		{w0, w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15}	<=	{w0, w1,  w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15};	


always@(posedge clk_in)
	if(!reset_n_in)
		begin
			word_p_out	<=	32'd0;
			word_out	<=	32'd0;
		end
	else if(working_en)
		begin
			word_p_out	<=	w0 ^ w4;
			word_out	<=	w0;
		end
	else
		begin
			word_p_out	<=	word_p_out;
			word_out	<=	word_out;
		end
	
	
	
always@(posedge clk_in)					
	if(!reset_n_in)
		current_state	<=	`IDLE;
	else
		current_state	<=	next_state;
		
always@(*)
	begin	
		next_state	=	`IDLE;
		case(current_state)
			`IDLE:
					if(start_in)
						next_state = `WORKING;
					else
						next_state = `IDLE;

			`WORKING:
					if(index_j_in == 'd63)
						next_state = `IDLE;
					else
						next_state = `WORKING;
			default:
					next_state = `IDLE;
		endcase
	end
	
always@(posedge clk_in)
	if(!reset_n_in)
		begin
			working_en				<=	1'b0;
			msg_exp_finished_out	<=	1'b0;
		end
	else 
		begin
			
			if(next_state == `WORKING)
				working_en <= 1'b1;
			else
				working_en	<=	1'b0;	
			
			if(current_state == `WORKING && next_state == `IDLE)
				msg_exp_finished_out	<=	'd1;
			else
				msg_exp_finished_out	<=	'd0;
		end
		
		
endmodule
module barrel_shifter
	(
		data_in,
		shift_number_in,
		data_after_shift_out
	);

input	[31 : 0]			data_in;
input	[4 : 0]				shift_number_in;
output	reg	[31 : 0]		data_after_shift_out;

always@(*)
	case(shift_number_in)
		5'b0_0000:	data_after_shift_out	<=	 data_in;
		5'b0_0001:	data_after_shift_out	<=	{data_in[31 - 1  : 0], data_in[31 : 31  - 1  + 1]};
		5'b0_0010:	data_after_shift_out	<=	{data_in[31 - 2  : 0], data_in[31 : 31  - 2  + 1]};
		5'b0_0011:	data_after_shift_out	<=	{data_in[31 - 3  : 0], data_in[31 : 31  - 3  + 1]};
		5'b0_0100:	data_after_shift_out	<=	{data_in[31 - 4  : 0], data_in[31 : 31  - 4  + 1]};
		5'b0_0101:	data_after_shift_out	<=	{data_in[31 - 5  : 0], data_in[31 : 31  - 5  + 1]};
		5'b0_0110:	data_after_shift_out	<=	{data_in[31 - 6  : 0], data_in[31 : 31  - 6  + 1]};
		5'b0_0111:	data_after_shift_out	<=	{data_in[31 - 7  : 0], data_in[31 : 31  - 7  + 1]};
		5'b0_1000:	data_after_shift_out	<=	{data_in[31 - 8  : 0], data_in[31 : 31  - 8  + 1]};
		5'b0_1001:	data_after_shift_out	<=	{data_in[31 - 9  : 0], data_in[31 : 31  - 9  + 1]};
		5'b0_1010:	data_after_shift_out	<=	{data_in[31 - 10 : 0], data_in[31 : 31  - 10 + 1]};
		5'b0_1011:	data_after_shift_out	<=	{data_in[31 - 11 : 0], data_in[31 : 31  - 11 + 1]};
		5'b0_1100:	data_after_shift_out	<=	{data_in[31 - 12 : 0], data_in[31 : 31  - 12 + 1]};
		5'b0_1101:	data_after_shift_out	<=	{data_in[31 - 13 : 0], data_in[31 : 31  - 13 + 1]};
		5'b0_1110:	data_after_shift_out	<=	{data_in[31 - 14 : 0], data_in[31 : 31  - 14 + 1]};
		5'b0_1111:	data_after_shift_out	<=	{data_in[31 - 15 : 0], data_in[31 : 31  - 15 + 1]};
		5'b1_0000:	data_after_shift_out	<=	{data_in[31 - 16 : 0], data_in[31 : 31  - 16 + 1]};
		5'b1_0001:	data_after_shift_out	<=	{data_in[31 - 17 : 0], data_in[31 : 31  - 17 + 1]};
		5'b1_0010:	data_after_shift_out	<=	{data_in[31 - 18 : 0], data_in[31 : 31  - 18 + 1]};
		5'b1_0011:	data_after_shift_out	<=	{data_in[31 - 19 : 0], data_in[31 : 31  - 19 + 1]};
		5'b1_0100:	data_after_shift_out	<=	{data_in[31 - 20 : 0], data_in[31 : 31  - 20 + 1]};
		5'b1_0101:	data_after_shift_out	<=	{data_in[31 - 21 : 0], data_in[31 : 31  - 21 + 1]};
		5'b1_0110:	data_after_shift_out	<=	{data_in[31 - 22 : 0], data_in[31 : 31  - 22 + 1]};
		5'b1_0111:	data_after_shift_out	<=	{data_in[31 - 23 : 0], data_in[31 : 31  - 23 + 1]};
		5'b1_1000:	data_after_shift_out	<=	{data_in[31 - 24 : 0], data_in[31 : 31  - 24 + 1]};
		5'b1_1001:	data_after_shift_out	<=	{data_in[31 - 25 : 0], data_in[31 : 31  - 25 + 1]};
		5'b1_1010:	data_after_shift_out	<=	{data_in[31 - 26 : 0], data_in[31 : 31  - 26 + 1]};
		5'b1_1011:	data_after_shift_out	<=	{data_in[31 - 27 : 0], data_in[31 : 31  - 27 + 1]};
		5'b1_1100:	data_after_shift_out	<=	{data_in[31 - 28 : 0], data_in[31 : 31  - 28 + 1]};
		5'b1_1101:	data_after_shift_out	<=	{data_in[31 - 29 : 0], data_in[31 : 31  - 29 + 1]};
		5'b1_1110:	data_after_shift_out	<=	{data_in[31 - 30 : 0], data_in[31 : 31  - 30 + 1]};
		5'b1_1111:	data_after_shift_out	<=	{data_in[31 - 31 : 0], data_in[31 : 31  - 31 + 1]}; 
	endcase

endmodule
module tb_SM3_top;

reg						clk_in,
						reset_n_in,
						SM3_en_in;
reg		[31: 0]			msg_in;	
reg						msg_valid_in;
reg						is_last_word_in;	
reg		[1:0]			last_word_byte_in;	



wire	[255 : 0]		sm3_result_out;		
wire					sm3_finished_out;	

always #3 clk_in = ~clk_in;


initial
	begin
		clk_in				=	0;
		reset_n_in			=	0;
		SM3_en_in			=	0;
		msg_in				=	0;	
		msg_valid_in		=	0;
        is_last_word_in		=	0;	
        last_word_byte_in	=	0;
		#11
		reset_n_in			=	1;
		#22
		SM3_en_in			=	1;
		#44
		repeat(15) begin
			@(posedge clk_in) 
				begin 
					msg_valid_in 		= 1; 
					msg_in		 		= 32'h61626364;	
				end	
			end
		@(posedge clk_in) 
			begin 
				msg_valid_in 		= 1; 
				msg_in		 		= 32'h61626364;	
				is_last_word_in		=	1;
				last_word_byte_in	=	2'b11;				
			end		
		@(posedge clk_in) 
			begin 
				msg_valid_in 		= 0; 
				msg_in		 		= 0;	
				is_last_word_in		=	0;
				last_word_byte_in	=	0;
			end	
		wait(sm3_finished_out)
		#500
		@(posedge clk_in)
			SM3_en_in	=	0;		
		$stop;
	end

		
always@(*)		
	if(sm3_finished_out)
		begin
			$display("\n");
			$display("\n");		
			$display("     +--------------------------------------------------------------------------------------------+" );
			$display("     |  HASH of input message is %64h|!", sm3_result_out);
			$display("     +--------------------------------------------------------------------------------------------+" );
			if(sm3_result_out == 256'hdebe9ff9_2275b8a1_38604889_c18e5a4d_6fdb70e5_387e5765_293dcba3_9c0c5732)
				begin
					$display("\n");
					$display("\n");
					$display("     +---------------------------------------------+ ");
					$display("     |  HASH is the same with the expected value ! | ");
					$display("     |  TEST SUCESSFUL !                           | ");
					$display("     +---------------------------------------------+ ");
					$display("\n");
					$display("\n");					
				end
			else				
					$display("unexpected results, ERROR!");
			
		end

		
SM3_top uut
	(
		.clk_in(clk_in),
		.reset_n_in(reset_n_in),
		.SM3_en_in(SM3_en_in),
		.msg_in(msg_in),
		.msg_valid_in(msg_valid_in),
		.is_last_word_in(is_last_word_in),
		.last_word_byte_in(last_word_byte_in),
		.sm3_result_out(sm3_result_out),
		.sm3_finished_out(sm3_finished_out)
	);

endmodule
module therm_out(
    input wire clk,
    input wire rst,
    input wire [31:0] therm_in,
    output wire therm_do,
    output wire therm_fs
);

    reg [5:0] therm_in_buf;

    wire [4:0] therm_z;
    count_lead_zero #(.W_IN(32), .W_OUT(5)) clz (
        .in(therm_in_buf), 
        .out(therm_z)
    );

    reg [4:0] sr;
    reg [3:0] counter;
    always @(posedge clk) begin
        if (counter == 3'd4) begin
            counter <= 0;
            therm_in_buf <= therm_in;
            sr <= therm_z;
        end
        else begin
            counter <= counter + 1;
        end
        if (rst) begin
            counter <= 0;
        end
    end
    wire therm_fs_pre = counter == 0;
    wire therm_do_pre = sr[counter];

    sky130_fd_sc_hd__buf_16 fs_buf(
        .A(therm_fs_pre),
        .X(therm_fs)
    );

    sky130_fd_sc_hd__buf_16 do_buf(
        .A(therm_do_pre),
        .X(therm_do)
    );

endmodule
module count_lead_zero #(
    parameter W_IN = 64, // Must be power of 2, >=2
    parameter W_OUT = 6
) (
    input wire  [W_IN-1:0] in,
    output wire [W_OUT-1:0] out
);

generate
if (W_IN == 2) begin: base
    assign out = !in[1];
end else begin: recurse
    wire [W_OUT-2:0] half_count;
    wire [W_IN / 2-1:0] lhs = in[W_IN / 2 +: W_IN / 2];
    wire [W_IN / 2-1:0] rhs = in[0        +: W_IN / 2];
    wire left_empty = ~|lhs;

    count_lead_zero #(
        .W_IN (W_IN / 2)
    ) inner (
        .in  (left_empty ? rhs : lhs),
        .out (half_count)
    );

    assign out = {left_empty, half_count};
end
endgenerate

endmodule
module fifo_1d_64to32(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire [63:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    output wire [31:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);

    reg [63:0] fifo;
    reg fifo_full;
    reg fifo_empty;
    always @(posedge clk) begin
        if (fifo_empty) begin
            // Output invalid, if with valid input, fill input
            if (a_valid) begin
                fifo <= a_data;
                fifo_empty <= 1'b0;
                fifo_full <= 1'b1;
            end
        end
        else if (fifo_full) begin
            // Output valid, input not ready, if with valid output, shift
            if (b_ready) begin
                fifo <= {fifo[31:0], 32'b0};
                fifo_full <= 1'b0;
            end
        end
        else begin
            // Half empty, output valid, input ready only if output is ready
            if (b_ready && a_valid) begin
                fifo <= a_data;
                fifo_full <= 1'b1;
            end
            else if (b_ready) begin
                fifo_empty <= 1'b1;
            end
        end

        if (rst) begin
            fifo_full <= 1'b0;
            fifo_empty <= 1'b1;
        end
    end

    // RX data if fifo is empty
    assign a_ready = fifo_empty || (!fifo_full && b_ready);
    assign b_valid = !fifo_empty;
    assign b_data = fifo[63:32];

endmodule
module ram_1024_64(
        input wire clk,
        input wire rst,
        // Read port
        input wire [9:0] raddr,
        output wire [63:0] rd,
        // Write port
        input wire [9:0] waddr,
        input wire [63:0] wr,
        input wire we
   );

`ifdef SKY130
    reg rd_bypass_en;
    reg [63:0] rd_bypass;
    always @(posedge clk) begin
        rd_bypass <= wr;
        rd_bypass_en <= (raddr == waddr) && we;
    end

    wire [63:0] bank0_rd;
    wire [63:0] bank1_rd;
    wire bank0_wcs = !waddr[9] & we;
    wire bank1_wcs = waddr[9] & we;
    wire bank0_rcs = !waddr[9];
    wire bank1_rcs = waddr[9];

    sky130_sram_2kbyte_1rw1r_32x512_8 bank0_lo_mem(
        .clk0(clk),
        .csb0(!bank0_wcs),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[31:0]),
        .dout0(),
        .clk1(clk),
        .csb1(!bank0_rcs),
        .addr1(raddr[8:0]),
        .dout1(bank0_rd[31:0])
    );

    sky130_sram_2kbyte_1rw1r_32x512_8 bank0_hi_mem(
        .clk0(clk),
        .csb0(!bank0_wcs),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[63:32]),
        .dout0(),
        .clk1(clk),
        .csb1(!bank0_rcs),
        .addr1(raddr[8:0]),
        .dout1(bank0_rd[63:32])
    );

    sky130_sram_2kbyte_1rw1r_32x512_8 bank1_lo_mem(
        .clk0(clk),
        .csb0(!bank1_wcs),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[31:0]),
        .dout0(),
        .clk1(clk),
        .csb1(!bank1_rcs),
        .addr1(raddr[8:0]),
        .dout1(bank1_rd[31:0])
    );

    sky130_sram_2kbyte_1rw1r_32x512_8 bank1_hi_mem(
        .clk0(clk),
        .csb0(!bank1_wcs),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[63:32]),
        .dout0(),
        .clk1(clk),
        .csb1(!bank1_rcs),
        .addr1(raddr[8:0]),
        .dout1(bank1_rd[63:32])
    );

    assign rd = (rd_bypass_en) ? (rd_bypass) :
            (bank0_rcs) ? (bank0_rd) : (bank1_rd);
`else
    reg [63:0] mem [0:1023];
    reg [63:0] rd_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if ((raddr == waddr) && we) begin
                rd_reg <= wr;
            end
            else begin
                rd_reg <= mem[raddr];
            end
            // W
            if (we) begin
                mem[waddr] <= wr;
            end
        end
    end

    assign rd = rd_reg;
`endif

endmodule
module fifo_1d_32to64(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire [31:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    output wire [63:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);

    reg [63:0] fifo;
    reg fifo_full;
    reg fifo_empty;
    always @(posedge clk) begin
        if (fifo_empty) begin
            // Output invalid, if with valid input, fill input
            if (a_valid) begin
                fifo <= {a_data, 32'b0};
                fifo_empty <= 1'b0;
                fifo_full <= 1'b0;
            end
        end
        else if (fifo_full) begin
            // Output valid, input not ready, if with valid output, shift
            if (b_ready && a_valid) begin
                fifo <= {a_data, 32'b0};
                fifo_empty <= 1'b0;
                fifo_full <= 1'b0;
            end
            else if (b_ready) begin
                fifo_full <= 1'b0;
                fifo_empty <= 1'b1;
            end
        end
        else begin
            // Half empty, output not valid, input ready
            if (a_valid) begin
                fifo <= {fifo[63:32], a_data};
                fifo_full <= 1'b1;
                fifo_empty <= 1'b0;
            end
        end

        if (rst) begin
            fifo_full <= 1'b0;
            fifo_empty <= 1'b1;
        end
    end

    // RX data if fifo is empty
    assign a_ready = !fifo_full || (fifo_full && b_ready);
    assign b_valid = fifo_full;
    assign b_data = fifo;

endmodule
module ram_generic_1rw1r(
    input wire clk,
    input wire rst,
    // Read write port
    input wire [ABITS-1:0] addr0,
    input wire re0,
    output reg [DBITS-1:0] rd0,
    input wire [DBITS-1:0] wr0,
    input wire we0,
    // Read only port
    input wire [ABITS-1:0] addr1,
    input wire re1,
    output reg [DBITS-1:0] rd1
);

    parameter DBITS = 8;
    parameter ABITS = 12;
    localparam DEPTH = (1 << ABITS);

    reg [DBITS-1:0] mem [0:DEPTH-1];

    always @(posedge clk) begin
        if (!rst) begin
            if (re0) begin
                rd0 <= mem[addr0];
            end
            else if (we0) begin
                mem[addr0] <= wr0;
            end
            
            if (re1) begin
                rd1 <= mem[addr1];
            end
        end
    end

endmodule
module ram_32_56(
        input wire clk,
        input wire rst,
        // Read port
        input wire [4:0] raddr,
        input wire re,
        output reg [55:0] rd,
        // Write port
        input wire [4:0] waddr,
        input wire [55:0] wr,
        input wire we
   );

    reg [55:0] mem [0:31];

    always @(posedge clk) begin
        if (!rst) begin
            if (re) begin
                rd <= mem[raddr];
            end
            // W
            if (we) begin
                mem[waddr] <= wr;
            end
        end
    end

endmodule
module ram_128_23(
    input wire clk,
    input wire rst,
    // Read port
    input wire [6:0] raddr,
    output wire [22:0] rd,
    input wire re,
    // Write port
    input wire [6:0] waddr,
    input wire [22:0] wr,
    input wire we
);

`ifdef SKY130
    /*sky130_sram_1r1w_24x128 mem(
        .clk0(clk),
        .csb0(!we),
        .addr0(waddr),
        .din0(wr),
        .clk1(clk),
        .csb1(!re),
        .addr1(raddr),
        .dout1(rd)
    );*/

    // Use provided 32x256 macro
    wire [31:0] sram_rd;
    wire [31:0] sram_wr;
    sky130_sram_1kbyte_1rw1r_32x256_8 mem(
        .clk0(clk),
        .csb0(!we),
        .web0(!we),
        .wmask0(4'hF),
        .addr0({1'b0, waddr}),
        .din0(sram_wr),
        .dout0(),
        .clk1(clk),
        .csb1(!re),
        .addr1({1'b0, raddr}),
        .dout1(sram_rd)
    );
    assign rd = sram_rd[22:0];
    assign sram_wr = {9'd0, wr};
`else
    reg [22:0] mem [0:127];
    reg [22:0] rd_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if (we) begin
                mem[waddr] <= wr;
            end
            
            if (re) begin
                rd_reg <= mem[raddr];
            end
        end
    end

    assign rd = rd_reg;
`endif

endmodule
module ram_1024_8(
    input wire clk,
    input wire rst,
    // Read write port
    input wire [9:0] addr0,
    input wire re0,
    output wire [7:0] rd0,
    input wire [7:0] wr0,
    input wire we0,
    // Read only port
    input wire [9:0] addr1,
    input wire re1,
    output wire [7:0] rd1
);

`ifdef SKY130
    sky130_sram_1kbyte_1rw1r_8x1024_8 mem(
        .clk0(clk),
        .csb0(!(re0 || we0)),
        .web0(!we0),
        .wmask0(1'b1),
        .addr0(addr0),
        .din0(wr0),
        .dout0(rd0),
        .clk1(clk),
        .csb1(!re1),
        .addr1(addr1),
        .dout1(rd1)
    );
`else
    reg [7:0] mem [0:1023];
    reg [7:0] rd0_reg;
    reg [7:0] rd1_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if (re0) begin
                rd0_reg <= mem[addr0];
            end
            else if (we0) begin
                mem[addr0] <= wr0;
            end
            
            if (re1) begin
                rd1_reg <= mem[addr1];
            end
        end
    end

    assign rd0 = rd0_reg;
    assign rd1 = rd1_reg;
`endif

endmodule
module fifo_2d (
    input  wire             clk,
    input  wire             rst,
    input  wire [WIDTH-1:0] a_data,
    input  wire             a_valid,
    output wire             a_ready,
    output wire             a_almost_full,
    output wire             a_full,
    output wire [WIDTH-1:0] b_data,
    output wire             b_valid,
    input  wire             b_ready
);

    parameter WIDTH = 64;

    reg [WIDTH-1:0] fifo_top;
    reg [WIDTH-1:0] fifo_bottom;
    reg fifo_empty;
    reg fifo_full;
    reg [1:0] rd_ptr;
    always @(posedge clk) begin
        if (a_ready && a_valid) begin
            fifo_top <= a_data;
            fifo_bottom <= fifo_top;
            if (!(b_ready && b_valid)) begin
                // Enqueue
                if (fifo_empty) begin
                    rd_ptr <= 2'd0;
                    fifo_empty <= 1'b0;
                end
                else begin
                    rd_ptr <= 2'd1;
                    fifo_full <= 1'b1;
                end
            end
            else begin
                // Data coming in and out normally
            end
        end
        else if (b_ready && b_valid) begin
            // Dequeue
            if (fifo_full) begin
                fifo_full <= 1'b0;
                rd_ptr <= 2'd0;
            end
            else begin
                fifo_empty <= 1'b1;
                // no more data
            end
        end
        if (rst) begin
            fifo_full <= 1'b0;
            fifo_empty <= 1'b1;
            rd_ptr <= 2'd0;
        end
    end
    assign b_valid = !fifo_empty;
    assign b_data = (rd_ptr == 2'd0) ? fifo_top : fifo_bottom;
    assign a_ready = !fifo_full || b_ready;
    assign a_almost_full = !fifo_empty;
    assign a_full = fifo_full;

endmodule
module fifo_2d_fwft (
    input  wire             clk,
    input  wire             rst,
    input  wire [WIDTH-1:0] a_data,
    input  wire             a_valid,
    output wire             a_ready,
    output wire             a_almost_full,
    output wire [WIDTH-1:0] b_data,
    output wire             b_valid,
    input  wire             b_ready
);

    parameter WIDTH = 64;

    reg [WIDTH-1:0] fifo_top;
    reg [WIDTH-1:0] fifo_bottom;
    reg fifo_empty;
    reg fifo_full;
    reg [1:0] rd_ptr;
    always @(posedge clk) begin
        if (a_ready && a_valid) begin
            fifo_top <= a_data;
            fifo_bottom <= fifo_top;
            if (!b_ready) begin
                // Enqueue
                if (fifo_empty) begin
                    rd_ptr <= 2'd1;
                    fifo_empty <= 1'b0;
                end
                else if (fifo_full) begin
                    // Cry
                    $display("IF FIFO overflow!");
                end
                else begin
                    rd_ptr <= 2'd2;
                    fifo_full <= 1'b1;
                end
            end
            else begin
                // Data coming in and out normally
            end
        end
        else if (b_ready) begin
            // Dequeue
            if (fifo_empty) begin
                // Cry
            end
            else if (fifo_full) begin
                fifo_full <= 1'b0;
                rd_ptr <= 2'd1;
            end
            else begin
                fifo_empty <= 1'b1;
                rd_ptr <= 2'd0;
            end
        end
        if (rst) begin
            fifo_full <= 1'b0;
            fifo_empty <= 1'b1;
            rd_ptr <= 2'd0;
        end
    end
    assign b_valid = !fifo_empty || a_valid;
    assign b_data = (rd_ptr == 2'd0) ? a_data :
            (rd_ptr == 2'd1) ? fifo_top : fifo_bottom;
    assign a_ready = !fifo_full;
    assign a_almost_full = !fifo_empty;

endmodule
module ram_512_64(
    input wire clk,
    input wire rst,
    // Read port
    input wire [8:0] raddr,
    output wire [63:0] rd,
    input wire re,
    // Write port
    input wire [8:0] waddr,
    input wire [63:0] wr,
    input wire we
);

`ifdef SKY130
    reg rd_bypass_en;
    reg [63:0] rd_bypass;
    always @(posedge clk) begin
        rd_bypass <= wr;
        rd_bypass_en <= (raddr == waddr) && we;
    end

    wire [63:0] ram_rd;
    wire ram_re = (raddr != waddr) && re;

    sky130_sram_2kbyte_1rw1r_32x512_8 lo_mem(
        .clk0(clk),
        .csb0(!we),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[31:0]),
        .dout0(),
        .clk1(clk),
        .csb1(!ram_re),
        .addr1(raddr[8:0]),
        .dout1(ram_rd[31:0])
    );

    sky130_sram_2kbyte_1rw1r_32x512_8 hi_mem(
        .clk0(clk),
        .csb0(!we),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr[8:0]),
        .din0(wr[63:32]),
        .dout0(),
        .clk1(clk),
        .csb1(!ram_re),
        .addr1(raddr[8:0]),
        .dout1(ram_rd[63:32])
    );

    /*sky130_sram_4kbyte_1r1w_64x512 mem(
        .clk0(clk),
        .csb0(!we),
        .addr0(waddr),
        .din0(wr),
        .clk1(clk),
        .csb1(!ram_re),
        .addr1(raddr),
        .dout1(ram_rd)
    );*/

    assign rd = rd_bypass_en ? rd_bypass : ram_rd;
`else
    reg [63:0] mem [0:511];
    reg [63:0] rd_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if (re) begin
                if ((raddr == waddr) && we) begin
                    rd_reg <= wr;
                end
                else begin
                    rd_reg <= mem[raddr];
                end
            end
            // W
            if (we) begin
                mem[waddr] <= wr;
            end
        end
    end

    assign rd = rd_reg;
`endif

endmodule
module fifo_1d_64to22(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire         a_short,
    input  wire [63:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    output wire [21:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);
    // Transfer higher order byte to lower order
    reg [63:0] fifo;
    reg [1:0] fifo_level;
    wire fifo_almost_empty = (fifo_level == 1);
    wire fifo_empty = (fifo_level == 0);
    wire [1:0] full_level = a_short ? 2 : 3;

    always @(posedge clk) begin
        if (fifo_empty) begin
            // Output invalid, if with valid input, fill input
            if (a_valid) begin
                fifo <= a_data;
                fifo_level <= full_level;
            end
        end
        else if (fifo_almost_empty) begin
            // Almost empty, output valid, input ready only if output is ready
            if (b_ready && a_valid) begin
                fifo <= a_data;
                fifo_level <= full_level;
            end
            else if (b_ready) begin
                fifo_level <= 0;
            end
        end
        else begin
            // Output valid, input not ready, if with valid output, shift
            if (b_ready) begin
                fifo_level <= fifo_level - 1;
            end
        end

        if (rst) begin
            fifo_level <= 0;
        end
    end

    // RX data if fifo is empty
    assign a_ready = fifo_empty || (fifo_almost_empty && b_ready);
    assign b_valid = !fifo_empty;
    assign b_data =
            (fifo_level == 3) ? {2'b0, fifo[63:44]} :
            (fifo_level == 2) ? fifo[43:22] :
            (fifo_level == 1) ? fifo[21:0] : 22'bx;

endmodule
module fifo_1d_fwft (
    input  wire             clk,
    input  wire             rst,
    input  wire [WIDTH-1:0] a_data,
    input  wire             a_valid,
    output wire             a_ready,
    output wire [WIDTH-1:0] b_data,
    output wire             b_valid,
    input  wire             b_ready
);

    parameter WIDTH = 64;

    reg [WIDTH-1:0] fifo;
    reg fifo_full;
    always @(posedge clk) begin
        if (rst) begin
            fifo_full <= 1'b0;
        end
        else begin
            if (a_ready && a_valid) begin
                fifo <= a_data;
                if (!b_ready)
                    fifo_full <= 1'b1;
            end
            else if (b_ready)
                fifo_full <= 1'b0;
        end
    end
    assign b_valid = fifo_full || a_valid;
    assign b_data = fifo_full ? fifo : a_data;
    assign a_ready = !fifo_full;

endmodule
module fifo_nd (
    input  wire             clk,
    input  wire             rst,
    input  wire [WIDTH-1:0] a_data,
    input  wire             a_valid,
    output wire             a_ready,
    output wire             a_almost_full,
    output wire             a_full,
    output wire [WIDTH-1:0] b_data,
    output wire             b_valid,
    input  wire             b_ready
);

    parameter WIDTH = 64;
    parameter ABITS = 2;
    localparam DEPTH = (1 << ABITS);

    reg [WIDTH-1:0] fifo [0:DEPTH-1];
    reg [ABITS:0] fifo_level;
    reg [ABITS-1:0] wr_ptr;
    reg [ABITS-1:0] rd_ptr;

    wire a_active = a_ready && a_valid;
    wire b_active = b_ready && b_valid;

    wire fifo_empty = fifo_level == 0;
    wire fifo_almost_full = fifo_level == DEPTH - 1;
    wire fifo_full = fifo_level == DEPTH;

    always @(posedge clk) begin
        if (a_ready && a_valid)
            fifo[wr_ptr] <= a_data;
        if (a_active && !b_active)
            fifo_level <= fifo_level + 1;
        else if (!a_active && b_active)
            fifo_level <= fifo_level - 1;
        if (a_active)
            wr_ptr <= wr_ptr + 1;
        if (b_active)
            rd_ptr <= rd_ptr + 1;
        
        if (rst) begin
            wr_ptr <= 0;
            rd_ptr <= 0;
            fifo_level <= 0;
        end
    end
    assign b_valid = !fifo_empty;
    assign b_data = fifo[rd_ptr];
    assign a_ready = !fifo_almost_full;
    assign a_almost_full = fifo_almost_full;
    assign a_full = fifo_full;

endmodule
module ram_generic_1r1w(
    input wire clk,
    input wire rst,
    // Read port
    input wire [6:0] raddr,
    output wire [31:0] rd,
    input wire re,
    // Write port
    input wire [6:0] waddr,
    input wire [31:0] wr,
    input wire we
);

    parameter DBITS = 8;
    parameter ABITS = 12;
    localparam DEPTH = (1 << ABITS);

    reg [DBITS-1:0] mem [0:DEPTH-1];
    reg [DBITS-1:0] rd_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if (we) begin
                mem[waddr] <= wr;
            end
            
            if (re) begin
                rd_reg <= mem[raddr];
            end
        end
    end

    assign rd = rd_reg;

endmodule
module fifo_1d_16to64(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire [15:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    output wire [63:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);
    // Store from higher order byte to lower order
    reg [63:0] fifo;
    reg [2:0] fifo_level;
    wire fifo_full = (fifo_level == 4);

    wire [63:0] new_data =
            (fifo_level == 3) ? {fifo[63:16], a_data} :
            (fifo_level == 2) ? {fifo[63:32], a_data, 16'b0} :
            (fifo_level == 1) ? {fifo[63:48], a_data, 32'b0} :
            ((fifo_level == 0) || (fifo_level == 4)) ? {a_data, 48'b0} : 64'bx;

    always @(posedge clk) begin
        if (fifo_full) begin
            if (b_ready && a_valid) begin
                // Output ready, input valid, shiftin new data
                fifo <= new_data;
                fifo_level <= 1;
            end
            else if (b_ready) begin
                // Output ready, no input, output data
                fifo_level <= 0;
            end
        end
        else begin
            // Not full, output not valid, input ready
            if (a_valid) begin
                fifo <= new_data;
                fifo_level <= fifo_level + 1;
            end
        end

        if (rst) begin
            fifo_level <= 0;
        end
    end

    // RX data if fifo is empty
    assign a_ready = !fifo_full || (fifo_full && b_ready);
    assign b_valid = fifo_full;
    assign b_data = fifo;

endmodule
module ram_256_32(
    input wire clk,
    input wire rst,
    // Read port
    input wire [7:0] raddr,
    output wire [31:0] rd,
    input wire re,
    // Write port
    input wire [7:0] waddr,
    input wire [31:0] wr,
    input wire we
);

`ifdef SKY130
    // Collision avoidance policy: write take priority
    wire re_ca = ((raddr == waddr) && we) ? 1'b0 : re;

    // No 128_32 macro provided, so...
    sky130_sram_1kbyte_1rw1r_32x256_8 mem(
        .clk0(clk),
        .csb0(!we),
        .web0(!we),
        .wmask0(4'hF),
        .addr0(waddr),
        .din0(wr),
        .dout0(),
        .clk1(clk),
        .csb1(!re_ca),
        .addr1(raddr),
        .dout1(rd)
    );
`else
    reg [31:0] mem [0:127];
    reg [31:0] rd_reg;

    always @(posedge clk) begin
        if (!rst) begin
            if (we) begin
                mem[waddr] <= wr;
            end
            
            if (re) begin
                rd_reg <= mem[raddr];
            end
        end
    end

    assign rd = rd_reg;
`endif

endmodule
module fifo_1d_64to16(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire [63:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    output wire [15:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);
    // Transfer higher order byte to lower order
    reg [63:0] fifo;
    reg [2:0] fifo_level;
    wire fifo_almost_empty = (fifo_level == 1);
    wire fifo_empty = (fifo_level == 0);

    always @(posedge clk) begin
        if (fifo_empty) begin
            // Output invalid, if with valid input, fill input
            if (a_valid) begin
                fifo <= a_data;
                fifo_level <= 4;
            end
        end
        else if (fifo_almost_empty) begin
            // Almost empty, output valid, input ready only if output is ready
            if (b_ready && a_valid) begin
                fifo <= a_data;
                fifo_level <= 4;
            end
            else if (b_ready) begin
                fifo_level <= 0;
            end
        end
        else begin
            // Output valid, input not ready, if with valid output, shift
            if (b_ready) begin
                fifo_level <= fifo_level - 1;
            end
        end

        if (rst) begin
            fifo_level <= 0;
        end
    end

    // RX data if fifo is empty
    assign a_ready = fifo_empty || (fifo_almost_empty && b_ready);
    assign b_valid = !fifo_empty;
    assign b_data =
            (fifo_level == 4) ? fifo[63:48] :
            (fifo_level == 3) ? fifo[47:32] :
            (fifo_level == 2) ? fifo[31:16] :
            (fifo_level == 1) ? fifo[15:0] : 16'bx;

endmodule
module fifo_1d_22to64(
    input  wire         clk,
    input  wire         rst,
    // Incoming port
    input  wire [21:0]  a_data,
    input  wire         a_valid,
    output wire         a_ready,
    // Outgoing port
    input  wire         b_short,
    output wire [63:0]  b_data,
    output wire         b_valid,
    input  wire         b_ready
);
    // Store from higher order byte to lower order
    reg [63:0] fifo;
    reg [1:0] fifo_level;
    wire fifo_full = (b_short) ? (fifo_level == 2) : (fifo_level == 3);

    wire [63:0] new_data =
            ((fifo_level == 2) && !b_short) ? {fifo[63:22], a_data} :
            (fifo_level == 1) ? {fifo[63:44], a_data, 22'b0} :
            ((fifo_level == 0) || ((fifo_level == 3) || (b_short && (fifo_level == 2)))) ? {a_data[19:0], 44'b0} :
            64'bx;

    always @(posedge clk) begin
        if (fifo_full) begin
            if (b_ready && a_valid) begin
                // Output ready, input valid, shiftin new data
                fifo <= new_data;
                fifo_level <= 1;
            end
            else if (b_ready) begin
                // Output ready, no input, output data
                fifo_level <= 0;
            end
        end
        else begin
            // Not full, output not valid, input ready
            if (a_valid) begin
                fifo <= new_data;
                fifo_level <= fifo_level + 1;
            end
        end

        if (rst) begin
            fifo_level <= 0;
        end
    end

    // RX data if fifo is empty
    assign a_ready = !fifo_full || (fifo_full && b_ready);
    assign b_valid = fifo_full;
    assign b_data = fifo;

endmodule
module priority_arbiter #(
    parameter WIDTH = -1
) (
    input  wire [WIDTH-1:0] request,
    output wire [WIDTH-1:0] grant
);

    // Bit hack to get the lowest bit set
    assign grant = request & -request;

endmodule