dakies/OSS_Verilog huggingface dataset:
module arbiter (
	input clk,
	input reset,

	output [31:0] request_data,
	output stall,

	input [31:0] fetch_addr,
	input fetch_request,
	output fetch_data_valid,

	input [31:0] dmem_addr,
	input [31:0] dmem_write_data,
	input [3:0] dmem_write_mask,
	input dmem_request,
	input dmem_request_type,
	output dmem_data_valid,

	/* memory interface */
	output reg [31:0] mem_addr,
	output [3:0] mem_mask, /* write */
	output mem_enable,
	output reg mem_cmd,
	input [31:0] mem_data,
	output [31:0] mem_wdata,
	input mem_valid
);

	wire requests_pending = fetch_request | dmem_request;

	reg fetch_satisfied;
	reg memory_satisfied;

	localparam S_IDLE      = 3'd0;
	localparam S_SVC_FETCH = 3'd1;
	localparam S_SVC_MEM_R = 3'd2;
	localparam S_SVC_MEM_W = 3'd3;

	reg [2:0] state;
	reg [2:0] next_state;

	always @ (posedge clk) begin
		if (reset) begin
			state <= S_IDLE;
		end else begin
			state <= next_state;
		end
	end

	always @ (posedge clk) begin
		if (reset) begin
			fetch_satisfied  <= 1;
		end else if (state == S_IDLE && next_state != S_IDLE) begin
			fetch_satisfied  <= (fetch_request ? 0 : 1);
		end else if (state == S_SVC_FETCH && mem_valid) begin
			fetch_satisfied  <= 1;
		end

		if (reset) begin
			memory_satisfied <= 1;
		end else if (state == S_IDLE && next_state != S_IDLE) begin
			memory_satisfied <= (dmem_request ? 0 : 1);
		end else if (state == S_SVC_MEM_R && mem_valid) begin
			memory_satisfied <= 1;
		end
	end

	always @ (*) begin
		case (state)
			S_IDLE:
				if (requests_pending)
					if (fetch_request)
						next_state = S_SVC_FETCH;
					else if (dmem_request)
						if (dmem_request_type == `MEM_REQ_WRITE)
							next_state = S_SVC_MEM_W;
						else
							next_state = S_SVC_MEM_R;
					else
						next_state = S_IDLE;
				else
					next_state = S_IDLE;

			S_SVC_FETCH:
				if (!fetch_satisfied)
					next_state = S_SVC_FETCH;
				else if (dmem_request)
					if (dmem_request_type == `MEM_REQ_WRITE)
						next_state = S_SVC_MEM_W;
					else
						next_state = S_SVC_MEM_R;
				else
					next_state = S_IDLE;

			S_SVC_MEM_R:
				if (!memory_satisfied)
					next_state = S_SVC_MEM_R;
				else
					next_state = S_IDLE;

			S_SVC_MEM_W:
				/* allow writes to go... write through */
				next_state = S_IDLE;

			default:
				next_state = S_IDLE;
		endcase
	end

	always @ (*) begin
		case (state)
			S_SVC_FETCH:
				mem_addr = fetch_addr;
			S_SVC_MEM_R, S_SVC_MEM_W:
				mem_addr = dmem_addr;
			default:
				mem_addr = 0;			
		endcase
	end

	assign stall = (next_state != S_IDLE);

	assign fetch_data_valid = (state == S_SVC_FETCH & mem_valid);
	assign dmem_data_valid = (state == S_SVC_MEM_R & mem_valid);

	assign request_data = mem_data;
	assign mem_wdata = dmem_write_data;
	assign mem_enable = (state == S_SVC_FETCH && !fetch_satisfied) || 
	                    (state == S_SVC_MEM_R && !memory_satisfied) ||
	                    (state == S_SVC_MEM_W);
	assign mem_mask = dmem_write_mask;

	always @ (*) begin
		case (state)
			S_SVC_FETCH: mem_cmd = `MEM_CMD_READ;
			S_SVC_MEM_R: mem_cmd = `MEM_CMD_READ;
			S_SVC_MEM_W: mem_cmd = `MEM_CMD_WRITE;
			default: mem_cmd = 0;
		endcase
	end
	
endmodule
module alu_dec (
	output reg [4:0] alu_sel,
	output [1:0] mul_sel,
	output reg [31:0] op1,
	output reg [31:0] op2,
	input [31:0] rs1,
	input [31:0] rs2,
	input [31:0] inst
);

	wire [6:0] opcode = inst[6:0];
	wire [9:7] funct3 = inst[9:7];
	wire [4:0] shamt = inst[14:10];

	wire [11:0] imm12 = inst[21:10];
	wire [31:0] s_ext_imm = imm12[11] ? {20'hFFFFF, imm12} : {20'b0, imm12};
	wire [31:0] z_ext_imm = {20'b0, imm12};
	wire [19:0] lui_imm = inst[26:7];

	wire [4:0] imm12hi = inst[31:27];
	wire [6:0] imm12lo = inst[16:10];
	wire [11:0] split_imm12 = {imm12hi, imm12lo};

	reg [3:0] compute_sel;
	
	always @ (*) begin
		/* op1 */
		op1 = (opcode == `OPCODE_LUI) ? {12'b0, lui_imm} : rs1;

		/* op2 */
		case (opcode)
			`OPCODE_OP_IMM:
				case (funct3)
					/* sign-extended immediate */
					`F3_ADD, `F3_SLT, `F3_SLTU, `F3_XOR, `F3_OR, `F3_AND:
						op2 = s_ext_imm;
					/* shifts */
					`F3_SLL, `F3_SR:
						op2 = shamt;
					default:
						op2 = 0;
				endcase

			`OPCODE_STORE:
				op2 = split_imm12;
			`OPCODE_LOAD:
				op2 = imm12;
			`OPCODE_OP:
				/* subtraction */
				op2 = (funct3 == `F3_ADD && inst[16]) ? -rs2 : rs2;
			default:
				op2 = rs2;
		endcase

		/* compute_sel, for integer computes with[out] immediates */
		case (funct3)
			`F3_ADD:  compute_sel = `ALU_ADD;
			`F3_SLL:  compute_sel = `ALU_SLL;
			`F3_SLT:  compute_sel = `ALU_SLT;
			`F3_SLTU: compute_sel = `ALU_SLTU;
			`F3_XOR:  compute_sel = `ALU_XOR;
			`F3_SR:   compute_sel = inst[16] ? `ALU_SRA : `ALU_SRL;
			`F3_OR:   compute_sel = `ALU_OR;
			`F3_AND:  compute_sel = `ALU_AND;
			default:  compute_sel = `ALU_NONE;
		endcase
		
		/* alu_sel */
		case (opcode)
			`OPCODE_OP:
				/* inst[10] indicates the multiplication/division operations */
				if (inst[10] == 1'b1) begin
					case (funct3)
						`F3_MUL:  alu_sel = `ALU_MUL;
						`F3_DIV:  alu_sel = `ALU_DIV;
						`F3_DIVU: alu_sel = `ALU_DIVU;
						`F3_REM:  alu_sel = `ALU_REM;
						`F3_REMU: alu_sel = `ALU_REMU;
						default: alu_sel = `ALU_NONE;
					endcase
				end else begin
					alu_sel = compute_sel;
				end

			`OPCODE_OP_IMM:
				alu_sel = compute_sel;

			`OPCODE_LOAD, `OPCODE_STORE:
				alu_sel = `ALU_ADD;

			`OPCODE_LUI:
				alu_sel = `ALU_LUI;

			`OPCODE_BRANCH:
				case (funct3)
					`F3_BEQ, `F3_BNE:   alu_sel = `ALU_XOR;
					`F3_BLT, `F3_BGE:   alu_sel = `ALU_SLT;
					`F3_BLTU, `F3_BGEU: alu_sel = `ALU_SLTU;
					default: alu_sel = `ALU_NONE;
				endcase

			default:
				alu_sel = `ALU_NONE;
		endcase

	end

	/* mul_sel - it maps directly on the funct bits */
	assign mul_sel = inst[8:7];
endmodule
module translater (
	input clk,
	input reset,

	input vm_enable,
	input launch_translation,

	output reg [31:0] physical_address,
	input [31:0] virtual_address,

	output translation_complete,

	input [31:0] ptbr,
	input flush_tlb,

	/* memory interface */
	output reg [31:0] translate_addr,
	output translate_mem_enable,
	input [31:0] request_data,
	input translate_data_valid
);

	wire [31:0] tlb_output_address;
	wire tlb_hit;
	wire translation_required;

	localparam S_IDLE      = 2'd0;
	localparam S_TLB       = 2'd1;
	localparam S_TRAN_PPN1 = 2'd2;
	localparam S_TRAN_PPN0 = 2'd3;

	reg [31:0] address;

	reg [1:0] state;
	reg [1:0] next_state;

	always @ (posedge clk) begin
		if (reset)
			state <= S_IDLE;
		else
			state <= next_state;
	end

	always @ (posedge clk) begin
		if (reset)
			address <= 0;
		else if (state == S_IDLE && launch_translation)
			address <= virtual_address;
	end

	always @ (*) begin
		case (state)
			S_IDLE:
				next_state = launch_translation ? S_TLB : S_IDLE;

			S_TLB:
				if (tlb_hit)
					next_state = S_IDLE;
				else if (translation_required)
					next_state = S_TRAN_PPN1;
				else
					next_state = S_TLB;
			
			S_TRAN_PPN1:
				next_state = translate_data_valid ? S_TRAN_PPN0 : S_TRAN_PPN1;

			S_TRAN_PPN0:
				next_state = translate_data_valid ? S_IDLE : S_TRAN_PPN0;

			default:
				next_state = S_IDLE;
		endcase
	end

	reg [31:0] ppn1_data;
	reg ppn1_valid;

	reg [31:0] ppn0_data;
	reg ppn0_valid;

	always @ (posedge clk) begin
		if (reset) begin
			ppn1_valid <= 0;
			ppn1_data <= 0;
			ppn0_valid <= 0;
			ppn0_data <= 0;
		end else begin
			if (state == S_IDLE && launch_translation) begin
				ppn1_valid <= 0;
				ppn1_data <= 0;
				ppn0_valid <= 0;
				ppn0_data <= 0;
			end else if (state == S_TRAN_PPN1 && translate_data_valid) begin
				ppn1_data <= request_data;
				ppn1_valid <= 1;
			end else if (state == S_TRAN_PPN0 && translate_data_valid) begin
				ppn0_data <= request_data;
				ppn0_valid <= 1;
			end
		end
	end

	always @ (*) begin
		case (state)
			S_TRAN_PPN1:
				translate_addr = {ptbr[31:12], address[31:22], 2'b0};
			S_TRAN_PPN0:
				translate_addr = {ppn1_data[31:12], address[21:12], 2'b0};
			default:
				translate_addr = 32'b0;
		endcase
	end

	assign translate_mem_enable = (state == S_TRAN_PPN1) || (state == S_TRAN_PPN0);
	assign translation_complete = (state == S_IDLE);

	/* Latch inferred. */
	always @ (*) begin
		if (translation_complete)
			if (tlb_hit)
				physical_address = {tlb_output_address[31:12], address[11:0]};
			else
				physical_address = {ppn0_data[31:12], address[11:0]};
	end

	/* TLB */

	wire tlb_enable = (state == S_TLB);

	tlb tlb(.clk(clk), .reset(reset), .flush(flush_tlb),
		.vm_enable(vm_enable), .enable(tlb_enable),
		.virtual_address(virtual_address), .physical_address(tlb_output_address),
		.tlb_hit(tlb_hit), .translation_required(translation_required),
		.translated_address(physical_address), .translation_complete(translation_complete));

endmodule
module decode (
	input [31:0] inst,

	output [4:0] rs1,
	output [4:0] rs2,
	output [4:0] wd,

	output reg [2:0] wb_sel,

	output rf_wr_en,

	output memory_request,
	output memory_request_type,

	output reg pcr_enable,
	output reg [1:0] pcr_cmd
);
	wire [6:0] opcode = inst[6:0];
	assign rs1 = inst[26:22];
	assign rs2 = inst[21:17];
	assign wd = (opcode == `OPCODE_JAL) ? 5'd1 : inst[31:27];
	
	assign rf_wr_en = (opcode == `OPCODE_OP ||
	                   opcode == `OPCODE_OP_IMM ||
	                   opcode == `OPCODE_LUI ||
	                   opcode == `OPCODE_LOAD ||
	                   opcode == `OPCODE_JAL ||
	                   opcode == `OPCODE_JALR ||
			   opcode == `OPCODE_PCR);

	assign memory_request = (opcode == `OPCODE_LOAD || 
	                         opcode == `OPCODE_STORE);
	
	assign memory_request_type = (opcode == `OPCODE_STORE) ? 
	                             `MEM_REQ_WRITE : `MEM_REQ_READ;

	always @ (*) begin
		case (opcode)
			`OPCODE_OP, `OPCODE_OP_IMM, `OPCODE_LUI:
				wb_sel = `WB_ALU;
			`OPCODE_LOAD:
				wb_sel = `WB_MEM;
			`OPCODE_JAL, `OPCODE_JALR:
				wb_sel = `WB_PC4;
			`OPCODE_PCR:
				wb_sel = `WB_PCR;
			default:
				wb_sel = 0;
		endcase
	end

	always @ (*) begin
		if (opcode == `OPCODE_PCR) begin
			pcr_enable = 1;
			/* funct3 map for cmd */
			pcr_cmd = inst[8:7];
		end else begin
			pcr_enable = 0;
			pcr_cmd = 2'b00;
		end
	end

endmodule
module mem (
	input clk,
	input reset,
	
	input [31:0] addr,
	input [3:0] mask,
	input enable,

	input cmd,
	input [31:0] write_data,
	output reg [31:0] load_data,
	output reg valid
);
	localparam MEMORY_SIZE = (1 << 14);

	reg [31:0] memory [MEMORY_SIZE - 1:0];

	wire [29:0] word_addr = addr[31:2];

	initial begin
		/* Loads by word addresses. Address 0x302c corresponds to 0x0c0b. */
		$readmemh("mem.hex", memory);
	end

	always @ (*) begin
		if (enable && cmd == `MEM_CMD_READ) begin
			load_data = memory[word_addr];
			valid = 1;
		end else begin
			load_data = 32'b0;
			valid = 0;
		end
	end

	wire [31:0] expanded_mask = {mask[3] ? 8'hFF : 8'h00,
	                             mask[2] ? 8'hFF : 8'h00,
	                             mask[1] ? 8'hFF : 8'h00,
	                             mask[0] ? 8'hFF : 8'h00};

	wire [31:0] to_be_written = (memory[word_addr] & ~expanded_mask) | (write_data & expanded_mask);

	always @ (*) begin
		if (enable && cmd == `MEM_CMD_WRITE) begin
			memory[word_addr] = to_be_written;
		end
	end

endmodule
module tlb (
	input clk,
	input reset,
	input flush,

	input vm_enable,
	input enable,
	input [31:0] virtual_address,
	output reg [31:0] physical_address,

	output reg tlb_hit,
	output reg translation_required,
	input [31:0] translated_address,
	input translation_complete
);

	localparam TLB_ENTRIES = 4;

	reg [1:0] entry; /* Next one to replace */
	reg [40:0] tlb [TLB_ENTRIES-1:0];

	integer i, j;

	always @ (posedge clk) begin
		if (reset || flush) begin
			for (j = 0; j < TLB_ENTRIES; j = j + 1) begin
				tlb[j] = 41'b0;
			end
			entry = 0;
		end
	end

	localparam S_CHECK = 0;
	localparam S_WAIT  = 1;

	reg state;
	reg next_state;

	always @ (posedge clk) begin
		if (reset)
			state <= S_CHECK;
		else
			state <= next_state;
	end

	always @ (*) begin
		case (state)
			S_CHECK: begin
				tlb_hit = 0;
				/* Check each TLB entry for a match */
				for (i = 0; i < TLB_ENTRIES; i = i + 1) begin
					if (virtual_address[31:12] == tlb[i][39:20] && tlb[i][40]) begin
						physical_address = {tlb[i][19:0], virtual_address[11:0]};
						tlb_hit = 1;
					end
				end

				translation_required = !tlb_hit && enable && vm_enable;
				next_state = !translation_required ? S_CHECK : S_WAIT;
			end

			S_WAIT:
				next_state = translation_complete ? S_CHECK : S_WAIT;

			default:
				next_state = S_CHECK;
		endcase
	end

	always @ (*) begin
		if (state == S_WAIT && translation_complete) begin
			tlb[entry] = {1'b1, virtual_address[31:12], translated_address[31:12]};
			entry = entry + 1;
		end
	end

endmodule
module top (
	input clk,
	input reset
);

	wire stall;

	wire [31:0] fetch_addr;
	wire fetch_request;
	wire fetch_data_valid;
	
	wire [31:0] memory_addr;
	wire [31:0] write_data;
	wire [3:0] write_mask;
	wire memory_request;
	wire memory_request_type;
	wire memory_data_valid;

	wire[31:0] request_data;

	wire [31:0] ptbr;
	wire vm_enable;

	datapath dpath(.clk(clk), .reset(reset), .stall(stall),
		.fetch_addr(fetch_addr), .fetch_request(fetch_request), .fetch_data_valid(fetch_data_valid),
		.dmem_addr(memory_addr), .dmem_write_data(write_data), .dmem_write_mask(write_mask),
		.dmem_request(memory_request), .dmem_request_type(memory_request_type), .dmem_data_valid(memory_data_valid),
		.request_data(request_data), .vm_enable(vm_enable), .ptbr(ptbr), .flush_tlb(flush_tlb));

	memory_system ms(.clk(clk), .reset(reset), .stall(stall),
		.request_data(request_data),
		.vm_enable(vm_enable), .ptbr(ptbr), .flush_tlb(flush_tlb),
		.fetch_addr(fetch_addr), .fetch_request(fetch_request), .fetch_data_valid(fetch_data_valid),
		.dmem_addr(memory_addr), .dmem_write_data(write_data), .dmem_write_mask(write_mask),
		.dmem_request(memory_request), .dmem_request_type(memory_request_type), .dmem_data_valid(memory_data_valid));

endmodule
module regfile (
	input clk,
	input reset,
	output [31:0] rd1, 
	output [31:0] rd2,
	input [4:0] rs1, 
	input [4:0] rs2, 
	input [4:0] wd,
	input [31:0] w_data,
	input w_enable,
	input stall);

	/* Registers */
	reg [31:0] registers[31:0];

	always @ (posedge clk) begin
		if (w_enable & !stall & wd != 0)
			registers[wd] <= w_data;
	end

	/* Outputs */
	assign rd1 = rs1 == 0 ? 32'b0 : registers[rs1];
	assign rd2 = rs2 == 0 ? 32'b0 : registers[rs2];

endmodule
module data_memory (
	input [31:0] inst,
	input [31:0] data,
	input [31:0] addr,

	output [31:0] memory_addr,

	output reg [31:0] write_data,
	output reg [3:0] write_mask,

	input [31:0] load_data,
	output reg [31:0] output_data
);

	wire [2:0] funct3 = inst[9:7];
	wire [6:0] opcode = inst[6:0];

	assign memory_addr = {addr[31:2], 2'b0};

	reg [31:0] write_data_masked;

	always @ (*) begin
		case (funct3)
			`F3_SB: write_data_masked = data & 32'hFF;
			`F3_SH: write_data_masked = data & 32'hFFFF;
			`F3_SW: write_data_masked = data & 32'hFFFFFFFF;
			default: write_data_masked = 32'h0;
		endcase
	end

	always @ (*) begin
		case (addr[1:0])
			2'b00: write_data = write_data_masked;
			2'b01: write_data = write_data_masked << 8;
			2'b10: write_data = write_data_masked << 16;
			2'b11: write_data = write_data_masked << 24;
			default: write_data = 32'b0;
		endcase
	end

	always @ (*) begin
		case (funct3)
			`F3_SB: write_mask =    4'b1 << addr[1:0];
			`F3_SH: write_mask =   4'b11 << addr[1:0];
			`F3_SW: write_mask = 4'b1111;
			default: write_mask = 0;
		endcase
	end

	reg [31:0] load_data_shifted;

	always @ (*) begin
		case (funct3)
			`F3_LB, `F3_LBU: case (addr[1:0])
				2'b00: load_data_shifted = {24'b0, load_data[7:0]};
				2'b01: load_data_shifted = {24'b0, load_data[15:8]};
				2'b10: load_data_shifted = {24'b0, load_data[23:16]};
				2'b11: load_data_shifted = {24'b0, load_data[31:24]};
				default: load_data_shifted = 32'b0;
			endcase
			
			`F3_LH, `F3_LHU: case (addr[1:0])
				2'b00: load_data_shifted = {16'b0, load_data[15:0]};
				2'b10: load_data_shifted = {16'b0, load_data[31:16]};
				default: load_data_shifted = 32'b0;
			endcase

			`F3_LW: load_data_shifted = load_data;

			default: load_data_shifted = 32'b0;
		endcase
	end

	wire [31:0] lds = load_data_shifted;

	always @ (*) begin
		case (funct3)
			`F3_LB: output_data = lds[7] ? {24'hFFFFFF, lds[7:0]} : {24'h0, lds[7:0]};
			`F3_LH: output_data = lds[15] ? {16'hFFFF, lds[15:0]} : {16'h0, lds[15:0]};
			`F3_LW, `F3_LHU, `F3_LBU: output_data = lds;
			default: output_data = 32'b0;
		endcase
	end

endmodule
module alu (
	output reg [31:0] alu_out,
	input [31:0] op1,
	input [31:0] op2,
	input [31:0] multiplier_result,
	input [4:0] alu_sel
);
	always @ (*) begin
		case (alu_sel)
			`ALU_ADD:  alu_out = op1 + op2;
			`ALU_SLL:  alu_out = op1 << op2;
			`ALU_SLT:  alu_out = $signed(op1) < $signed(op2) ? 1 : 0;
			`ALU_SLTU: alu_out = op1 < op2 ? 1 : 0;
			`ALU_XOR:  alu_out = op1 ^ op2;
			`ALU_SRL:  alu_out = op1 >> op2;
			`ALU_SRA:  alu_out = $signed(op1) >>> op2;
			`ALU_OR:   alu_out = op1 | op2;
			`ALU_AND:  alu_out = op1 & op2;
			`ALU_MUL:  alu_out = multiplier_result;
			`ALU_DIV:  alu_out = $signed(op1) / $signed(op2);
			`ALU_DIVU: alu_out = op1 / op2;
			`ALU_REM:  alu_out = $signed(op1) % $signed(op2);
			`ALU_REMU: alu_out = op1 % op2;
			`ALU_LUI:  alu_out = op1 << 12;
			`ALU_NONE: alu_out = 0;
		endcase
	end

endmodule
module bshift_test;
	parameter n=32;
	reg instr_bit_25;
	wire [11:0] imm_value={5'd0,3'd6,4'd0};
	//reg [n-1:0] in;
	//reg [n-1:0] out;
	//reg [7:0] shiftby; // no. bits to be shifted
	//reg [n-1:0] junk;
	reg [n-1:0] Rm;
	reg [n-1:0] Rs;
	wire cin=1;
	wire c_to_alu;
	wire [n-1:0] operand2;
	
bshift #(32) bshift1(instr_bit_25,imm_value, Rm, Rs, operand2, cin, c_to_alu);

initial begin
	instr_bit_25 = 1;
	//imm_value =5 ;
	Rm= {3'd3,29'd1};
	Rs = 32'd100;
	//cin = 1;
	#2 $display("%b,%b",operand2, c_to_alu);
	#5;
	end
	endmodule
module mult_test;
parameter N = 7;

wire[N-1:0] result;
reg[N-1:0] Rn;
reg[N-1:0]  Rs;
reg[N-1:0] Rm;
reg A;
multiplier#(N) multiplier1( A, Rn, Rs, Rm, result);

initial begin
	Rn = 30;
	Rs = 20;
	Rm = 2;
	A = 1;
	#2 $display("(%d * %d) + %d = %d", Rm, Rs, Rn, result);
end

endmodule
module system (
	input            clk,
	input            resetn,
	output reg led1
);
	// set this to 0 for better timing but less performance/MHz
	parameter FAST_MEMORY = 1;

	// 4096 32bit words = 16kB memory
	parameter MEM_SIZE = 4096;

	wire mem_valid;
	wire mem_instr;
	reg mem_ready;
	wire [31:0] mem_addr;
	wire [31:0] mem_wdata;
	wire [3:0] mem_wstrb;
	reg [31:0] mem_rdata;

    

	wire mem_la_read;
	wire mem_la_write;
	wire [31:0] mem_la_addr;
	wire [31:0] mem_la_wdata;
	wire [3:0] mem_la_wstrb;

	picorv32 picorv32_core (
		.clk         (clk         ),
		.resetn      (resetn      ),
		.trap        (trap        ),
		.mem_valid   (mem_valid   ),
		.mem_instr   (mem_instr   ),
		.mem_ready   (mem_ready   ),
		.mem_addr    (mem_addr    ),
		.mem_wdata   (mem_wdata   ),
		.mem_wstrb   (mem_wstrb   ),
		.mem_rdata   (mem_rdata   ),
		.mem_la_read (mem_la_read ),
		.mem_la_write(mem_la_write),
		.mem_la_addr (mem_la_addr ),
		.mem_la_wdata(mem_la_wdata),
		.mem_la_wstrb(mem_la_wstrb)
	);


   reg [23:0] led_cnt = {24{1'b0}};

   always @(posedge clk)
   begin
      led_cnt <= led_cnt - 1'b1;
      
      if (led_cnt[23] == 1) 
        led1 <= 0;
      else
        led1 <= 1;
    end


	wire           trap;
	reg [7:0] out_byte;
	reg [0:0]      out_byte_en;
    wire [0:0]  trap_ila;
    
    assign trap_ila[0] = trap;

    ila_0 inst_ila_0 (.clk(clk), .probe0(out_byte), .probe1(trap_ila), .probe2(out_byte_en));


	reg [31:0] memory [0:MEM_SIZE-1];
	initial $readmemh("firmware.hex", memory);

	reg [31:0] m_read_data;
	reg m_read_en;

	generate if (FAST_MEMORY) begin
		always @(posedge clk) begin
			mem_ready <= 1;
			out_byte_en[0] <= 0;
			mem_rdata <= memory[mem_la_addr >> 2];
			if (mem_la_write && (mem_la_addr >> 2) < MEM_SIZE) begin
				if (mem_la_wstrb[0]) memory[mem_la_addr >> 2][ 7: 0] <= mem_la_wdata[ 7: 0];
				if (mem_la_wstrb[1]) memory[mem_la_addr >> 2][15: 8] <= mem_la_wdata[15: 8];
				if (mem_la_wstrb[2]) memory[mem_la_addr >> 2][23:16] <= mem_la_wdata[23:16];
				if (mem_la_wstrb[3]) memory[mem_la_addr >> 2][31:24] <= mem_la_wdata[31:24];
			end
			else
			if (mem_la_write && mem_la_addr == 32'h1000_0000) begin
				out_byte_en[0] <= 1;
				out_byte <= mem_la_wdata;
			end
		end
	end else begin
		always @(posedge clk) begin
			m_read_en <= 0;
			mem_ready <= mem_valid && !mem_ready && m_read_en;

			m_read_data <= memory[mem_addr >> 2];
			mem_rdata <= m_read_data;

			out_byte_en[0] <= 0;

			(* parallel_case *)
			case (1)
				mem_valid && !mem_ready && !mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin
					m_read_en <= 1;
				end
				mem_valid && !mem_ready && |mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin
					if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0];
					if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8];
					if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16];
					if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24];
					mem_ready <= 1;
				end
				mem_valid && !mem_ready && |mem_wstrb && mem_addr == 32'h1000_0000: begin
					out_byte_en[0] <= 1;
					out_byte <= mem_wdata;
					mem_ready <= 1;
				end
			endcase
		end
	end endgenerate
endmodule
module system_tb;
	reg clk = 1;
	always #5 clk = ~clk;

	reg resetn = 0;
	initial begin
		if ($test$plusargs("vcd")) begin
			$dumpfile("system.vcd");
			$dumpvars(0, system_tb);
		end
		repeat (100) @(posedge clk);
		resetn <= 1;
	end

//	wire trap;
//	wire [7:0] out_byte;
//	wire out_byte_en;

    wire led1;



	system uut (
		.clk        (clk        ),
		.led1(led1)
	);

//	always @(posedge clk) begin
//		if (resetn && out_byte_en) begin
//			$write("%c", out_byte);
//			$fflush;
//		end
//		if (resetn && trap) begin
//			$finish;
//		end
//	end
endmodule
module sirv_plic_top(
  input   clk,
  input   rst_n,

  input                      i_icb_cmd_valid,
  output                     i_icb_cmd_ready,
  input  [32-1:0]            i_icb_cmd_addr, 
  input                      i_icb_cmd_read, 
  input  [32-1:0]            i_icb_cmd_wdata,
  
  output                     i_icb_rsp_valid,
  input                      i_icb_rsp_ready,
  output [32-1:0]            i_icb_rsp_rdata,

  input   io_devices_0_0,
  input   io_devices_0_1,
  input   io_devices_0_2,
  input   io_devices_0_3,
  input   io_devices_0_4,
  input   io_devices_0_5,
  input   io_devices_0_6,
  input   io_devices_0_7,
  input   io_devices_0_8,
  input   io_devices_0_9,
  input   io_devices_0_10,
  input   io_devices_0_11,
  input   io_devices_0_12,
  input   io_devices_0_13,
  input   io_devices_0_14,
  input   io_devices_0_15,

  output  io_harts_0_0
);

wire plic_irq;
assign io_harts_0_0 = plic_irq;

localparam PLIC_IRQ_NUM = 17;// The number can be enlarged as long as not larger than 1024
wire [PLIC_IRQ_NUM-1:0] plic_irq_i = { 

                  io_devices_0_15  ,
                  io_devices_0_14  ,
                  io_devices_0_13  ,
                  io_devices_0_12  ,
                  io_devices_0_11  ,
                  io_devices_0_10  ,

                  io_devices_0_9  ,
                  io_devices_0_8  ,
                  io_devices_0_7  ,
                  io_devices_0_6  ,
                  io_devices_0_5  ,
                  io_devices_0_4  ,
                  io_devices_0_3  ,
                  io_devices_0_2  ,
                  io_devices_0_1  ,
                  io_devices_0_0  ,

                  1'b0 };// The IRQ0 must be tied to zero


sirv_plic_man #(
    .PLIC_PRIO_WIDTH   (3),
    .PLIC_IRQ_NUM      (PLIC_IRQ_NUM),
    .PLIC_IRQ_NUM_LOG2 (6),
    .PLIC_ICB_RSP_FLOP (1),
    .PLIC_IRQ_I_FLOP   (1),
    .PLIC_IRQ_O_FLOP   (1) 
) u_sirv_plic_man(
    .clk              (clk            ),      
    .rst_n            (rst_n          ),

    .icb_cmd_valid  (i_icb_cmd_valid),
    .icb_cmd_addr   (i_icb_cmd_addr[24-1:0] ),
    .icb_cmd_read   (i_icb_cmd_read ),
    .icb_cmd_wdata  (i_icb_cmd_wdata),
    .icb_rsp_ready  (i_icb_rsp_ready),
                    
    .icb_rsp_valid  (i_icb_rsp_valid),
    .icb_cmd_ready  (i_icb_cmd_ready),
    .icb_rsp_rdata  (i_icb_rsp_rdata),

    .plic_irq_i (plic_irq_i),
    .plic_irq_o (plic_irq   ) 
);


endmodule
module e203_subsys_plic(
  input                          plic_icb_cmd_valid,
  output                         plic_icb_cmd_ready,
  input  [`E203_ADDR_SIZE-1:0]   plic_icb_cmd_addr, 
  input                          plic_icb_cmd_read, 
  input  [`E203_XLEN-1:0]        plic_icb_cmd_wdata,
  input  [`E203_XLEN/8-1:0]      plic_icb_cmd_wmask,
  //
  output                         plic_icb_rsp_valid,
  input                          plic_icb_rsp_ready,
  output                         plic_icb_rsp_err,
  output [`E203_XLEN-1:0]        plic_icb_rsp_rdata,

  output plic_ext_irq,

  input  wdg_irq_a,
  input  rtc_irq_a,

  input  qspi0_irq, 
  input  qspi1_irq,
  input  qspi2_irq,

  input  uart0_irq,                
  input  uart1_irq,                
  input  uart2_irq,                

  input  pwm_irq_0,
  input  pwm_irq_1,
  input  pwm_irq_2,
  input  pwm_irq_3,

  input  i2c0_mst_irq,
  input  i2c1_mst_irq,

  input  gpioA_irq,
  input  gpioB_irq,

  input  clk,
  input  rst_n
  );

  assign plic_icb_rsp_err     = 1'b0;

  wire  wdg_irq_r;
  wire  rtc_irq_r;

  sirv_gnrl_sync # (
  .DP(`E203_ASYNC_FF_LEVELS),
  .DW(1)
  ) u_rtc_irq_sync(
      .din_a    (rtc_irq_a),
      .dout     (rtc_irq_r),
      .clk      (clk  ),
      .rst_n    (rst_n) 
  );

  sirv_gnrl_sync # (
  .DP(`E203_ASYNC_FF_LEVELS),
  .DW(1)
  ) u_wdg_irq_sync(
      .din_a    (wdg_irq_a),
      .dout     (wdg_irq_r),
      .clk      (clk  ),
      .rst_n    (rst_n) 
  );

  wire plic_irq_i_0  = wdg_irq_r;
  wire plic_irq_i_1  = rtc_irq_r;
  wire plic_irq_i_2  = uart0_irq;
  wire plic_irq_i_3  = uart1_irq;
  wire plic_irq_i_4  = uart2_irq;
  wire plic_irq_i_5  = qspi0_irq;
  wire plic_irq_i_6  = qspi1_irq;   
  wire plic_irq_i_7  = qspi2_irq;
  wire plic_irq_i_8  = pwm_irq_0;
  wire plic_irq_i_9  = pwm_irq_1;
  wire plic_irq_i_10 = pwm_irq_2;
  wire plic_irq_i_11 = pwm_irq_3;
  wire plic_irq_i_12 = i2c0_mst_irq;
  wire plic_irq_i_13 = i2c1_mst_irq;
  wire plic_irq_i_14 = gpioA_irq;
  wire plic_irq_i_15 = gpioB_irq; 

  sirv_plic_top u_sirv_plic_top(
    .clk             (clk   ),
    .rst_n           (rst_n ),
  
    .i_icb_cmd_valid (plic_icb_cmd_valid),
    .i_icb_cmd_ready (plic_icb_cmd_ready),
    .i_icb_cmd_addr  (plic_icb_cmd_addr ),
    .i_icb_cmd_read  (plic_icb_cmd_read ),
    .i_icb_cmd_wdata (plic_icb_cmd_wdata),
    
    .i_icb_rsp_valid (plic_icb_rsp_valid),
    .i_icb_rsp_ready (plic_icb_rsp_ready),
    .i_icb_rsp_rdata (plic_icb_rsp_rdata),
  
    .io_devices_0_0  (plic_irq_i_0 ),
    .io_devices_0_1  (plic_irq_i_1 ),
    .io_devices_0_2  (plic_irq_i_2 ),
    .io_devices_0_3  (plic_irq_i_3 ),
    .io_devices_0_4  (plic_irq_i_4 ),
    .io_devices_0_5  (plic_irq_i_5 ),
    .io_devices_0_6  (plic_irq_i_6 ),
    .io_devices_0_7  (plic_irq_i_7 ),
    .io_devices_0_8  (plic_irq_i_8 ),
    .io_devices_0_9  (plic_irq_i_9 ),
    .io_devices_0_10 (plic_irq_i_10),
    .io_devices_0_11 (plic_irq_i_11),
    .io_devices_0_12 (plic_irq_i_12),
    .io_devices_0_13 (plic_irq_i_13),
    .io_devices_0_14 (plic_irq_i_14),
    .io_devices_0_15 (plic_irq_i_15),

    .io_harts_0_0    (plic_ext_irq ) 
  );

  endmodule
module e203_ifu_minidec(

  //////////////////////////////////////////////////////////////
  // The IR stage to Decoder
  input  [`E203_INSTR_SIZE-1:0] instr,
  
  //////////////////////////////////////////////////////////////
  // The Decoded Info-Bus


  output dec_rs1en,
  output dec_rs2en,
  output [`E203_RFIDX_WIDTH-1:0] dec_rs1idx,
  output [`E203_RFIDX_WIDTH-1:0] dec_rs2idx,

  output dec_mulhsu,
  output dec_mul   ,
  output dec_div   ,
  output dec_rem   ,
  output dec_divu  ,
  output dec_remu  ,

  output dec_rv32,
  output dec_bjp,
  output dec_jal,
  output dec_jalr,
  output dec_bxx,
  output [`E203_RFIDX_WIDTH-1:0] dec_jalr_rs1idx,
  output [`E203_XLEN-1:0] dec_bjp_imm 

  );

  e203_exu_decode u_e203_exu_decode(

  .i_instr(instr),
  .i_pc(`E203_PC_SIZE'b0),
  .i_prdt_taken(1'b0), 
  .i_muldiv_b2b(1'b0), 

  .i_misalgn (1'b0),
  .i_buserr  (1'b0),

  .dbg_mode  (1'b0),

  .dec_misalgn(),
  .dec_buserr(),
  .dec_ilegl(),

  .dec_rs1x0(),
  .dec_rs2x0(),
  .dec_rs1en(dec_rs1en),
  .dec_rs2en(dec_rs2en),
  .dec_rdwen(),
  .dec_rs1idx(dec_rs1idx),
  .dec_rs2idx(dec_rs2idx),
  .dec_rdidx(),
  .dec_info(),  
  .dec_imm(),
  .dec_pc(),

`ifdef E203_HAS_NICE//{
  .dec_nice   (),
  .nice_xs_off(1'b0),  
  .nice_cmt_off_ilgl_o(),
`endif//}

  .dec_mulhsu(dec_mulhsu),
  .dec_mul   (dec_mul   ),
  .dec_div   (dec_div   ),
  .dec_rem   (dec_rem   ),
  .dec_divu  (dec_divu  ),
  .dec_remu  (dec_remu  ),

  .dec_rv32(dec_rv32),
  .dec_bjp (dec_bjp ),
  .dec_jal (dec_jal ),
  .dec_jalr(dec_jalr),
  .dec_bxx (dec_bxx ),

  .dec_jalr_rs1idx(dec_jalr_rs1idx),
  .dec_bjp_imm    (dec_bjp_imm    )  
  );


endmodule
module e203_extend_csr(

  // The Handshake Interface 
  input          nice_csr_valid,
  output         nice_csr_ready,

  input   [31:0] nice_csr_addr,
  input          nice_csr_wr,
  input   [31:0] nice_csr_wdata,
  output  [31:0] nice_csr_rdata,

  input  clk,
  input  rst_n
  );

  assign nice_csr_ready = 1'b1;
  assign nice_csr_rdata = 32'b0;


endmodule
module uart_interrupt
#(
    parameter TX_FIFO_DEPTH = 32,
    parameter RX_FIFO_DEPTH = 32
)
(
    input  wire                           clk_i,
    input  wire                           rstn_i,

    // registers
    input  wire [2:0]                     IER_i,             // interrupt enable register

    // control logic
    input  wire                           error_i,
    input  wire [$clog2(RX_FIFO_DEPTH):0] rx_elements_i,
    input  wire [$clog2(TX_FIFO_DEPTH):0] tx_elements_i,
    input  wire [1:0]                     trigger_level_i,

    input  wire [3:0]                     clr_int_i,         // one hot

    output wire                           interrupt_o,
    output wire [3:0]                     IIR_o
);

    reg [3:0] iir_n;
    reg [3:0] iir_q;
    reg trigger_level_reached;

    always @(*) begin
        trigger_level_reached = 1'b0;
        case (trigger_level_i)
            2'b00:
                if ($unsigned(rx_elements_i) == 1)
            	    trigger_level_reached = 1'b1;
            2'b01:
                if ($unsigned(rx_elements_i) == 4)
            	    trigger_level_reached = 1'b1;
            2'b10:
                if ($unsigned(rx_elements_i) == 8)
            	    trigger_level_reached = 1'b1;
            2'b11:
                if ($unsigned(rx_elements_i) == 14)
            	    trigger_level_reached = 1'b1;
        endcase
    end


    always @(*) begin
        if (clr_int_i == 4'b0) begin
            // Receive data parity error
            if (IER_i[2] & error_i)
            	iir_n = 4'b1100;
            // Trigger level reached in FIFO mode
            else if (IER_i[0] & trigger_level_reached)
            	iir_n = 4'b1000;
            // Transmitter holding register empty
            else if (IER_i[1] & (tx_elements_i == 0))
            	iir_n = 4'b0100;
            else
            	iir_n = iir_q;
        end else begin
            iir_n = iir_q & ~clr_int_i;
        end
    end


    always @(posedge clk_i or negedge rstn_i) begin
        if (~rstn_i)
       	    iir_q <= 4'b0;
        else
       	    iir_q <= iir_n;
    end

    assign IIR_o = iir_q;
    assign interrupt_o = iir_q[2] | iir_q[3];

endmodule
module spi_master_clkgen (
    input  wire         clk,
    input  wire         rstn,
    input  wire         en,
    input  wire [7:0]   clk_div,
    input  wire         clk_div_valid,
    output reg          spi_clk,
    output reg          spi_fall,
    output reg          spi_rise
);

    reg [7:0] counter_trgt;
    reg [7:0] counter_trgt_next;
    reg [7:0] counter;
    reg [7:0] counter_next;

    reg       spi_clk_next;
    reg       running;

    always @(*) begin
        spi_rise = 1'b0;
        spi_fall = 1'b0;

        if (clk_div_valid)
            counter_trgt_next = clk_div;
        else
            counter_trgt_next = counter_trgt;

        if (counter == counter_trgt) begin
            counter_next = 0;
            spi_clk_next = ~spi_clk;

            if (spi_clk == 1'b0)
                spi_rise = running;
            else
                spi_fall = running;

	end else begin
            counter_next = counter + 1;
            spi_clk_next = spi_clk;
        end
    end

    always @(posedge clk or negedge rstn) begin
        if (rstn == 1'b0) begin
            counter_trgt <= 'h0;
            counter      <= 'h0;
            spi_clk      <= 1'b0;
            running      <= 1'b0;
	end else begin
            counter_trgt <= counter_trgt_next;

            if (!((spi_clk == 1'b0) && ~en)) begin
                running <= 1'b1;
                spi_clk <= spi_clk_next;
                counter <= counter_next;
	    end else
                running <= 1'b0;
        end
    end


endmodule
module timer_cntrl (
    input  wire       clk_i,
    input  wire       rstn_i,

    input  wire       cfg_start_i,
    input  wire       cfg_stop_i,
    input  wire       cfg_rst_i,
    input  wire       cfg_update_i,
    input  wire       cfg_arm_i,

    output reg        ctrl_cnt_upd_o,
    output reg        ctrl_all_upd_o,
    output wire       ctrl_active_o,
    output reg        ctrl_rst_o,
    output wire       ctrl_arm_o,

    input  wire       cnt_update_i,

    output wire [7:0] status_o
);

    reg r_active;
    reg r_pending;
    
    assign ctrl_arm_o    = cfg_arm_i;
    assign status_o      = {6'h00, r_pending};
    assign ctrl_active_o = r_active;
    
    always @(*) begin : proc_sm
        if (cfg_start_i && !r_active) begin
            ctrl_rst_o     = 1'b1;
            ctrl_cnt_upd_o = 1'b1;
            ctrl_all_upd_o = 1'b1;
	end else begin
            ctrl_rst_o     = cfg_rst_i;
            ctrl_cnt_upd_o = cfg_update_i;
            ctrl_all_upd_o = cnt_update_i;
        end
    end


    always @(posedge clk_i or negedge rstn_i) begin : proc_r_active
        if (~rstn_i) begin
            r_active  <= 0;
            r_pending <= 0;
        end else begin
            if (cfg_start_i)
                r_active <= 1;
            else if (cfg_stop_i)
                r_active <= 0;

            if (cnt_update_i && !cfg_update_i)
                r_pending <= 0;
            else if (cfg_update_i)
                r_pending <= 1;
        end
    end
    
endmodule
module prescaler (
    input  wire       clk_i,
    input  wire       rstn_i,
    input  wire       ctrl_active_i,
    input  wire       ctrl_update_i,
    input  wire       ctrl_rst_i,
    input  wire [7:0] cfg_presc_i,
    input  wire       event_i,
    output reg        event_o
);

    reg [7:0] r_presc;
    reg [7:0] r_counter;

    always @(posedge clk_i or negedge rstn_i) begin : proc_r_presc
        if (~rstn_i)
            r_presc <= 0;
        else if (ctrl_update_i)    //if first enable or explicit update is iven
            r_presc <= cfg_presc_i;
    end


    always @(posedge clk_i or negedge rstn_i) begin : proc_r_counter
        if (~rstn_i) begin
            r_counter <= 0;
            event_o   <= 0;
	end else if (ctrl_rst_i) begin
            r_counter <= 0;
            event_o   <= 0;
	end else if (ctrl_active_i) begin
            if (event_i) begin
		if (r_presc == 0) begin
                    event_o   <= 1'b1;
		end else if (r_counter == r_presc) begin
                    event_o   <= 1'b1;
                    r_counter <= 0;
		end else begin
                    event_o   <= 1'b0;
                    r_counter <= r_counter + 1;
                end
	    end else begin
                event_o <= 1'b0;
	    end
	end else begin
            r_counter <= 0;
            event_o   <= 0;
        end
    end


endmodule
module ram_2w4r #(
    parameter   RAM_TYPE        = -1,
    parameter   RAM_DEPTH       = -1,
    parameter   RAM_ADDR_WIDTH  = -1,
    parameter   RAM_DATA_WIDTH  = -1
)(
    input                           clk,
    input                           rst_n,

    input                           r0_val,
    input   [RAM_ADDR_WIDTH-1:0]    r0_addr,
    output  [RAM_DATA_WIDTH-1:0]    r0_data,

    input                           r1_val,
    input   [RAM_ADDR_WIDTH-1:0]    r1_addr,
    output  [RAM_DATA_WIDTH-1:0]    r1_data,

    input                           r2_val,
    input   [RAM_ADDR_WIDTH-1:0]    r2_addr,
    output  [RAM_DATA_WIDTH-1:0]    r2_data,

    input                           r3_val,
    input   [RAM_ADDR_WIDTH-1:0]    r3_addr,
    output  [RAM_DATA_WIDTH-1:0]    r3_data,

    input                           w0_val,
    input   [RAM_ADDR_WIDTH-1:0]    w0_addr,
    input   [RAM_DATA_WIDTH-1:0]    w0_data,

    input                           w1_val,
    input   [RAM_ADDR_WIDTH-1:0]    w1_addr,
    input   [RAM_DATA_WIDTH-1:0]    w1_data    
);

ram_2w2r #(
    .RAM_TYPE       (RAM_TYPE       ),
    .RAM_DEPTH      (RAM_DEPTH      ),
    .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH ),
    .RAM_DATA_WIDTH (RAM_DATA_WIDTH )
) mem0              (
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .r0_val         (r0_val         ),
    .r0_addr        (r0_addr        ),
    .r0_data        (r0_data        ),
    .r1_val         (r1_val         ),
    .r1_addr        (r1_addr        ),
    .r1_data        (r1_data        ),
    .w0_val         (w0_val         ),
    .w0_addr        (w0_addr        ),
    .w0_data        (w0_data        ),
    .w1_val         (w1_val         ),
    .w1_addr        (w1_addr        ),
    .w1_data        (w1_data        )
);

ram_2w2r #(
    .RAM_TYPE       (RAM_TYPE       ),
    .RAM_DEPTH      (RAM_DEPTH      ),
    .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH ),
    .RAM_DATA_WIDTH (RAM_DATA_WIDTH )
) mem1              (
    .clk            (clk            ),
    .rst_n          (rst_n          ),
    .r0_val         (r2_val         ),
    .r0_addr        (r2_addr        ),
    .r0_data        (r2_data        ),
    .r1_val         (r3_val         ),
    .r1_addr        (r3_addr        ),
    .r1_data        (r3_data        ),
    .w0_val         (w0_val         ),
    .w0_addr        (w0_addr        ),
    .w0_data        (w0_data        ),
    .w1_val         (w1_val         ),
    .w1_addr        (w1_addr        ),
    .w1_data        (w1_data        )
);

endmodule
module cnt_block #(
    parameter   BLOCK_WIDTH     = 2,
    parameter   BLOCK_IND_WIDTH = 10,
    parameter   BLOCK_LEVEL     = -1,
    parameter   BLOCK_WIDTH_LOG = clogb2(BLOCK_WIDTH)
)(
    input       [BLOCK_IND_WIDTH*BLOCK_WIDTH-1:0]       cnts_in,

    output      [BLOCK_IND_WIDTH+BLOCK_WIDTH_LOG-1:0]   cnt_out
);

generate 
    if (BLOCK_WIDTH == 2) begin
        assign cnt_out = {{BLOCK_WIDTH_LOG{1'b0}}, cnts_in[BLOCK_IND_WIDTH * BLOCK_WIDTH - 1 -: BLOCK_IND_WIDTH]} +
                   {{BLOCK_WIDTH_LOG{1'b0}}, cnts_in[BLOCK_IND_WIDTH-1:0]};
    end
    else begin
        assign cnt_out = {(BLOCK_IND_WIDTH+BLOCK_WIDTH_LOG){1'b0}};
    end
endgenerate


function integer clogb2;
input integer val;
begin
    clogb2 = 1;
    for (val = val/2; val > 1; val = val/2) begin
        clogb2 = clogb2 + 1;
    end
end
endfunction

endmodule
module cnt_wnd # (
    parameter   VECT_WIDTH          = -1,
    parameter   VECT_IND_WIDTH      = -1,
    parameter   BLOCK_WIDTH         = 2
) (
    input   [VECT_WIDTH-1:0]        vect_in,
    input                           select_set_in,

    output  [VECT_IND_WIDTH-1:0]    cnt_out
);

wire    [VECT_WIDTH-1:0]        vect_wnd_set_bits;

genvar ii;

generate
    for (ii = 0; ii < VECT_WIDTH; ii = ii + 1) begin: vect_wnd_set_bits_gen
        assign vect_wnd_set_bits[ii] = (vect_in[ii] == select_set_in);
    end
endgenerate


cnt_set #(
    .VECT_WIDTH         (VECT_WIDTH         ),
    .VECT_IND_WIDTH     (VECT_IND_WIDTH     ),
    .BLOCK_WIDTH        (BLOCK_WIDTH        )
) cnt_set (
    .vect_in            (vect_wnd_set_bits  ),
    .cnt_out            (cnt_out            )
);

endmodule
module cnt_set #(
    parameter   VECT_WIDTH      = 64,
    parameter   VECT_IND_WIDTH  = 4,
    parameter   BLOCK_WIDTH     = 2
)(
    input   [VECT_WIDTH-1:0]        vect_in,

    output  [VECT_IND_WIDTH-1:0]    cnt_out
);

localparam BLOCK_DEPTH      = clog(VECT_WIDTH, BLOCK_WIDTH);
localparam BLOCK_WIDTH_LOG  = clog(BLOCK_WIDTH, 2);

wire [VECT_WIDTH-1:0]    cnt_level[BLOCK_DEPTH:0];


assign cnt_out     = cnt_level[BLOCK_DEPTH][VECT_IND_WIDTH-1:0];

genvar ii, jj;


generate begin
    for (ii = 0; ii < BLOCK_DEPTH; ii = ii + 1) begin: level_gen
        if (ii == 0) begin
            assign cnt_level[ii] = vect_in;
        end

        // wire [VECT_WIDTH/pow(BLOCK_WIDTH,ii)-1:0] val_out_prev_level;
        for (jj = 0; jj < pow(BLOCK_WIDTH, BLOCK_DEPTH-ii-1); jj = jj + 1) begin: block_gen
            cnt_block # (
                .BLOCK_WIDTH        (BLOCK_WIDTH        ),
                .BLOCK_IND_WIDTH    (ii + 1             ),
                .BLOCK_LEVEL        (ii                 )
            ) cnt_block (
                .cnts_in    (cnt_level[ii][(jj+1)*(BLOCK_WIDTH*(ii+1))-1 :jj*BLOCK_WIDTH*(ii+1)]),
                .cnt_out    (cnt_level[ii+1][(jj+1)*(ii+1+BLOCK_WIDTH_LOG)-1:jj*(ii+1+BLOCK_WIDTH_LOG)])
            );
        end
    end
end
endgenerate

function integer clog;
input integer val;
input integer base;
begin
    clog = 1;
    for (val = val/base; val > 1; val = val/base) begin
        clog = clog + 1;
    end
end
endfunction


function integer pow;
input integer base;
input integer val;
begin
    pow = 1;
    for (val = val; val > 0; val = val - 1) begin
        pow = pow*base;
    end
end
endfunction


endmodule
module ff_wnd # (
    parameter   VECT_WIDTH          = -1,
    parameter   VECT_IND_WIDTH      = -1,
    parameter   BLOCK_WIDTH         = 2
) (
    input   [VECT_WIDTH-1:0]        vect_in,
    input                           select_set_in,
    input   [VECT_IND_WIDTH-1:0]    head_in,

    output                          val_out,
    output  [VECT_IND_WIDTH-1:0]    ind_out
);

wire    [VECT_WIDTH-1:0]        vect_wnd_set_bits_1;
wire                            val_out_1;
wire    [VECT_IND_WIDTH-1:0]    ind_out_1;

wire    [VECT_WIDTH-1:0]        vect_wnd_set_bits_2;
wire                            val_out_2;
wire    [VECT_IND_WIDTH-1:0]    ind_out_2;

genvar ii;

generate
    for (ii = 0; ii < VECT_WIDTH; ii = ii + 1) begin: vect_wnd_set_bits_gen
        assign vect_wnd_set_bits_1[ii] = (vect_in[ii] == select_set_in) &
                                         (ii >= head_in);


        assign vect_wnd_set_bits_2[ii] = (vect_in[ii] == select_set_in);
    end
endgenerate


ff_set #(
    .VECT_WIDTH         (VECT_WIDTH         ),
    .VECT_IND_WIDTH     (VECT_IND_WIDTH     ),
    .BLOCK_WIDTH        (BLOCK_WIDTH        )
) sfs1 (
    .vect_in            (vect_wnd_set_bits_1    ),
    .val_out            (val_out_1              ),
    .ind_out            (ind_out_1              )
);

ff_set #(
    .VECT_WIDTH         (VECT_WIDTH         ),
    .VECT_IND_WIDTH     (VECT_IND_WIDTH     ),
    .BLOCK_WIDTH        (BLOCK_WIDTH        )
) sfs2 (
    .vect_in            (vect_wnd_set_bits_2    ),
    .val_out            (val_out_2              ),
    .ind_out            (ind_out_2              )
);

assign  val_out = val_out_1 | val_out_2;
assign  ind_out = val_out_1 ? ind_out_1 : ind_out_2;

endmodule
module ff_block #(
    parameter   BLOCK_WIDTH = 4,
    parameter   BLOCK_IND_WIDTH = 10,
    parameter   BLOCK_LEVEL = -1
)(
    input       [BLOCK_WIDTH-1:0]                   ind_val_in,
    input       [BLOCK_IND_WIDTH*BLOCK_WIDTH-1:0]   ind_flat_in,

    output                                          val_out,
    output      [BLOCK_IND_WIDTH-1:0]               ind_out
);

localparam  INPUT_DIR_NUM_WIDTH = clogb2(BLOCK_WIDTH);
localparam  INPUT_DIR_SHIFT     = clogb2(BLOCK_WIDTH)*BLOCK_LEVEL;

wire    [BLOCK_WIDTH-1:0]           single_valid_bus;
wire    [BLOCK_IND_WIDTH-1:0]       selected_ind;
wire    [BLOCK_IND_WIDTH-1:0]       ind_matr[BLOCK_WIDTH-1:0];
wire    [INPUT_DIR_NUM_WIDTH-1:0]   selected_direction;
wire    [BLOCK_WIDTH-1:0]           ind_in_revert_matrix[BLOCK_IND_WIDTH-1:0];
wire    [INPUT_DIR_NUM_WIDTH-1:0]   direction_matrix[BLOCK_WIDTH-1:0];
wire    [BLOCK_WIDTH-1:0]           direction_matrix_inv[INPUT_DIR_NUM_WIDTH-1:0];

genvar ii, jj;

assign val_out = |ind_val_in;

//assign ind_out = (selected_direction << INPUT_DIR_SHIFT) + selected_ind;
assign ind_out = (selected_direction << INPUT_DIR_SHIFT) + selected_ind;


// Vector with at most one valid bit set
generate begin
    for (ii = 0; ii < BLOCK_WIDTH; ii = ii + 1) begin: single_valid_bus_gen
        if (ii == 0) begin
            assign single_valid_bus[ii] = ind_val_in[ii];
        end
        else begin
            assign single_valid_bus[ii] = ~(|ind_val_in[ii-1:0]) & ind_val_in[ii];
        end
    end
end
endgenerate


// Parallel OR input indexes masked by a vector with at most one valid bit set
generate begin
    for (ii = 0; ii < BLOCK_WIDTH; ii = ii + 1) begin: ind_matr_gen
        assign ind_matr[ii] = ind_flat_in[(ii+1)*BLOCK_IND_WIDTH-1:ii*BLOCK_IND_WIDTH];
    end


    for (ii = 0; ii < BLOCK_IND_WIDTH; ii = ii + 1) begin: selected_ind_gen
        for (jj = 0; jj < BLOCK_WIDTH; jj = jj + 1) begin: ind_in_revert_gen
            assign ind_in_revert_matrix[ii][jj] = ind_matr[jj][ii] & single_valid_bus[jj];
        end
        assign selected_ind[ii] = |ind_in_revert_matrix[ii];
    end
end
endgenerate

// Get Input Direction number curresponding to a set bit in single_valid_bus
generate begin
    for (ii = 0; ii < BLOCK_WIDTH; ii = ii + 1) begin: direction_matrix_gen
        wire    [31:0]  tmp_index = ii;
        assign direction_matrix[ii] = tmp_index[INPUT_DIR_NUM_WIDTH-1:0];
    end

    for (ii = 0; ii < INPUT_DIR_NUM_WIDTH; ii = ii + 1) begin: selected_direction_gen
        for (jj = 0; jj < BLOCK_WIDTH; jj = jj + 1) begin: direction_matrix_gen
            assign direction_matrix_inv[ii][jj] = direction_matrix[jj][ii] & single_valid_bus[jj];
        end
        assign selected_direction[ii] = |direction_matrix_inv[ii];
    end
end
endgenerate

// clogb2 function
`include "clogb2.vh"

endmodule
module femtoPLL #(
    parameter freq = 50
 ) (
    input wire pclk,
    output wire clk
 );

 wire clk_feedback;
 wire clk_internal;

 PLLE2_ADV #(
    .CLKFBOUT_MULT(freq/10),  // Multiply value for all CLKOUT (2-64)
    .DIVCLK_DIVIDE(1),        // Master division value , (1-56)
    .CLKOUT0_DIVIDE(10),      // 
    .BANDWIDTH("OPTIMIZED"),  // OPTIMIZED, HIGH, LOW
    .CLKFBOUT_PHASE(0.0),     // Phase offset in degrees of CLKFB, (-360-360)
    .CLKIN1_PERIOD(10.0),     // Input clock period in ns to ps resolution
    .CLKOUT0_DUTY_CYCLE(0.5),
    .CLKOUT0_PHASE(0.0),
    .REF_JITTER1(0.0),      // Reference input jitter in UI (0.000-0.999)
    .STARTUP_WAIT("FALSE")  // Delayu DONE until PLL Locks, ("TRUE"/"FALSE")
 ) genclock(
     .CLKOUT0(clk_internal),
     .CLKFBOUT(clk_feedback), // 1-bit output, feedback clock
     .CLKIN1(pclk),
     .PWRDWN(1'b0),
     .RST(1'b0),
     .CLKFBIN(clk_feedback),  // 1-bit input, feedback clock
     
     .CLKOUT1(), .CLKOUT2(), .CLKOUT3(), .CLKOUT4(), .CLKOUT5(),
     .DO(), .DRDY(), .LOCKED(), 
     .CLKIN2(), .CLKINSEL(),
     .DADDR(), .DCLK(), .DEN(), .DI(), .DWE()
 );

 BUFG bufg(
     .I(clk_internal),
     .O(clk)
 );

 endmodule
module femtoPLL #(
 parameter freq = 60
) (
 input 	wire pclk,
 output wire clk	   
);
   assign clk = pclk;   
endmodule