data/full_repos/permissive/448204225/src/riscv_core/Riscv151.v
1: b'%Error: data/full_repos/permissive/448204225/src/riscv_core/Riscv151.v:1: Cannot find include file: Opcode.vh\n`include "Opcode.vh" \n         ^~~~~~~~~~~\n        ... Looked in:\n             data/full_repos/permissive/448204225/src/riscv_core,data/full_repos/permissive/448204225/Opcode.vh\n             data/full_repos/permissive/448204225/src/riscv_core,data/full_repos/permissive/448204225/Opcode.vh.v\n             data/full_repos/permissive/448204225/src/riscv_core,data/full_repos/permissive/448204225/Opcode.vh.sv\n             Opcode.vh\n             Opcode.vh.v\n             Opcode.vh.sv\n             obj_dir/Opcode.vh\n             obj_dir/Opcode.vh.v\n             obj_dir/Opcode.vh.sv\n%Error: data/full_repos/permissive/448204225/src/riscv_core/Riscv151.v:273: Define or directive not defined: \'`OPC_LUI\'\n  assign addr_rs1_id_in = opcode_id_in == `OPC_LUI ? 5\'d0 : inst_id_in[19:15];\n                                          ^~~~~~~~\n%Error: data/full_repos/permissive/448204225/src/riscv_core/Riscv151.v:273: syntax error, unexpected \'?\', expecting TYPE-IDENTIFIER\n  assign addr_rs1_id_in = opcode_id_in == `OPC_LUI ? 5\'d0 : inst_id_in[19:15];\n                                                   ^\n%Error: Exiting due to 3 error(s)\n'
module Riscv151 #(
  parameter CPU_CLOCK_FREQ = 50_000_000,
  parameter RESET_PC       = 32'h4000_0000,
  parameter BAUD_RATE      = 115200,
  parameter BIOS_MIF_HEX   = "bios151v3.mif"
) (
  input clk,
  input rst,
  input FPGA_SERIAL_RX,
  output FPGA_SERIAL_TX,
  output [31:0] csr
);

  localparam BIOS_AWIDTH = 11;
  localparam BIOS_DWIDTH = 32;

  wire [BIOS_AWIDTH-1:0] bios_addra, bios_addrb;
  wire [BIOS_DWIDTH-1:0] bios_douta, bios_doutb;
  wire bios_ena;




  SYNC_ROM_DP #(
    .AWIDTH (BIOS_AWIDTH),
    .DWIDTH (BIOS_DWIDTH),
    .MIF_HEX(BIOS_MIF_HEX)
  ) bios_mem (
    .q0(bios_douta),
    .addr0(bios_addra),
    .en0(bios_ena),

    .q1(bios_doutb),
    .addr1(bios_addrb),
    .en1(1'b1),

    .clk(clk)
  );

  localparam DMEM_AWIDTH = 14;
  localparam DMEM_DWIDTH = 32;

  wire [DMEM_AWIDTH-1:0] dmem_addra;
  wire [DMEM_DWIDTH-1:0] dmem_dina, dmem_douta;
  wire [3:0] dmem_wea;





  SYNC_RAM_WBE #(
    .AWIDTH(DMEM_AWIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) dmem (
    .q(dmem_douta),
    .d(dmem_dina),
    .addr(dmem_addra),
    .wbe(dmem_wea),
    .en(1'b1),
    .clk(clk)
  );


  wire [7:0] uart_rx_data_out;
  wire uart_rx_data_out_valid;
  wire uart_rx_data_out_ready;

  uart_receiver #(
    .CLOCK_FREQ(CPU_CLOCK_FREQ),
    .BAUD_RATE (BAUD_RATE)
  ) uart_rx (
    .clk           (clk),
    .rst           (rst),
    .data_out      (uart_rx_data_out),
    .data_out_valid(uart_rx_data_out_valid),
    .data_out_ready(uart_rx_data_out_ready),
    .serial_in     (FPGA_SERIAL_RX)
  );


  wire [7:0] uart_tx_data_in;
  wire uart_tx_data_in_valid;
  wire uart_tx_data_in_ready;

  uart_transmitter #(
    .CLOCK_FREQ(CPU_CLOCK_FREQ),
    .BAUD_RATE (BAUD_RATE)
  ) uart_tx (
    .clk          (clk),
    .rst          (rst),
    .data_in      (uart_tx_data_in),
    .data_in_valid(uart_tx_data_in_valid),
    .data_in_ready(uart_tx_data_in_ready),
    .serial_out   (FPGA_SERIAL_TX)
  );




  localparam PC_WIDTH = 32;
  localparam INST_WIDTH = 32;

  wire ctrl_pc_src;
  wire [PC_WIDTH - 1:0] pc_new_if_in, pc_if_out;
  wire pc_en;



  PC #(
    .PC_WIDTH(PC_WIDTH),
    .RESET_PC_VAL(RESET_PC)
  ) pc (
    .clk(clk),
    .rst(rst),
    .pc_sel_in(ctrl_pc_src),
    .pc_en(pc_en),
    .pc_new_in(pc_new_if_in),
    .pc_out(pc_if_out)
  );

  localparam IMEM_AWIDTH = 14;
  localparam IMEM_DWIDTH = 32;

  wire [IMEM_AWIDTH-1:0] imem_addra, imem_addrb;
  wire [IMEM_DWIDTH-1:0] imem_douta, imem_doutb;
  wire [IMEM_DWIDTH-1:0] imem_dina, imem_dinb;
  wire [3:0] imem_wea, imem_web;
  wire imem_enb;

  wire [INST_WIDTH - 1:0] inst_if_out;
  wire inst_if_flush;





  SYNC_RAM_DP_WBE #(
    .AWIDTH(IMEM_AWIDTH),
    .DWIDTH(IMEM_DWIDTH)
  ) imem (
    .q0(imem_douta),
    .d0(imem_dina),
    .addr0(imem_addra),
    .wbe0(imem_wea),
    .en0(1'b1),

    .q1(imem_doutb),
    .d1(imem_dinb),
    .addr1(imem_addrb),
    .wbe1(imem_web),
    .en1(imem_enb),

    .clk(clk)
  );

  wire ctrl_imem_en_id_out;

  assign bios_addra = pc_if_out[13:2];
  assign imem_addrb = pc_if_out[15:2];
  assign imem_web = 4'h0;
  assign inst_if_out = inst_if_flush ? 32'b0 : (pc_if_out[30] == 1'b1) ? bios_douta : imem_doutb;

  assign imem_enb = ctrl_imem_en_id_out;
  assign bios_ena = ctrl_imem_en_id_out;



  wire [  PC_WIDTH - 1:0] pc_id_in;
  wire [INST_WIDTH - 1:0] inst_id_in;

  assign inst_id_in = inst_if_out;

  REGISTER_R_CE #(
    .N(PC_WIDTH),
    .INIT(0)
  ) if_id_pc (
    .d  (pc_if_out),
    .q  (pc_id_in),
    .ce (pc_en),
    .clk(clk),
    .rst(rst)
  );

  wire [DMEM_DWIDTH - 1:0] rs1_id_out, rs2_id_out;
  wire [DMEM_DWIDTH - 1:0] utype_rs1_id_out;
  wire [PC_WIDTH - 1:0] pc_branch_id_out, pc_id_out;
  wire [DMEM_DWIDTH - 1:0] imm_id_out;
  wire [INST_WIDTH - 1:0] pc_new_id_out;
  wire ctrl_zero_sel_id_out;
  wire ctrl_pc_src_id_out, ctrl_reg_we_id_out;
  wire ctrl_mem_write_id_out;
  wire ctrl_mem_read_id_out;
  wire ctrl_id_reg_flush_id_out;
  wire [1:0] ctrl_mem_to_reg_id_out;
  wire [1:0] ctrl_alu_op_id_out;
  wire [1:0] ctrl_alu_src_a_id_out, ctrl_alu_src_b_id_out;
  wire ctrl_forward_a_sel_id_out, ctrl_forward_b_sel_id_out;
  wire ctrl_forward_data_sel_id_out;
  wire ctrl_pc_en_id_out;

  wire ctrl_reg_we_id_in;
  reg [DMEM_DWIDTH - 1:0] rd_id_in;
  wire [4:0] addr_rs1_id_in, addr_rs2_id_in, addr_rd_id_in;

  wire ctrl_csr_we_id_out;
  wire ctrl_csr_rd_id_out;
  wire [11:0] csr_addr_id_out;
  wire [2:0] csr_func_id_out;

  wire [DMEM_DWIDTH - 1:0] alu_ex_out_id_in;
  wire ctrl_id_forward_a_sel, ctrl_id_forward_b_sel;
  wire [4:0] addr_rd_ex_in;

  ID #(
    .PC_WIDTH(PC_WIDTH),
    .INST_WIDTH(INST_WIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) id (
    .clk(clk),
    .rst(rst),

    .pc(pc_id_in),
    .addr_rs1(addr_rs1_id_in),
    .addr_rs2(addr_rs2_id_in),
    .addr_rd(addr_rd_id_in),
    .addr_rd_ex_in(addr_rd_ex_in),
    .inst(inst_id_in),
    .reg_we(ctrl_reg_we_id_in),
    .data_rd(rd_id_in),
    .forward_data_in(rd_id_in),
    .forward_a_sel_in(ctrl_id_forward_a_sel),
    .forward_b_sel_in(ctrl_id_forward_b_sel),

    .data_rs1(rs1_id_out),
    .data_rs2(rs2_id_out),
    .data_imm(imm_id_out),
    .data_pc(pc_id_out),
    .branch_pc_new(pc_new_id_out),
    .ctrl_zero_sel(ctrl_zero_sel_id_out),
    .ctrl_alu_op(ctrl_alu_op_id_out),
    .ctrl_pc_src(ctrl_pc_src_id_out),
    .ctrl_reg_we(ctrl_reg_we_id_out),
    .ctrl_alu_src_a(ctrl_alu_src_a_id_out),
    .ctrl_alu_src_b(ctrl_alu_src_b_id_out),
    .ctrl_mem_write(ctrl_mem_write_id_out),
    .ctrl_mem_read(ctrl_mem_read_id_out),
    .ctrl_mem_to_reg(ctrl_mem_to_reg_id_out),
    .ctrl_pc_en(ctrl_pc_en_id_out),
    .ctrl_imem_en(ctrl_imem_en_id_out),

    .ctrl_id_reg_flush(ctrl_id_reg_flush_id_out),

    .ctrl_csr_we(ctrl_csr_we_id_out),
    .ctrl_csr_rd(ctrl_csr_rd_id_out),
    .csr_addr(csr_addr_id_out),
    .csr_func(csr_func_id_out)
  );


  assign ctrl_pc_src = ctrl_pc_src_id_out;
  assign pc_en = ctrl_pc_en_id_out;

  assign inst_if_flush = ctrl_id_reg_flush_id_out;


  wire ctrl_reg_we_ex_in;
  wire [6:0] opcode_id_in;

  assign opcode_id_in = inst_id_in[6:0];

  assign addr_rs1_id_in = opcode_id_in == `OPC_LUI ? 5'd0 : inst_id_in[19:15];
  assign addr_rs2_id_in = inst_id_in[24:20];

  FORWARD #(
    .DWIDTH(DMEM_DWIDTH)
  ) forward (
    .rs1_addr_id(addr_rs1_id_in),
    .rs2_addr_id(addr_rs2_id_in),
    .opcode_id(opcode_id_in),
    .rd_addr_ex_in(addr_rd_ex_in),
    .rd_addr_id_in(addr_rd_id_in),
    .ctrl_reg_we_ex_in(ctrl_reg_we_ex_in),
    .ctrl_reg_we_id_in(ctrl_reg_we_id_in),
    .ex_forward_a_sel(ctrl_forward_a_sel_id_out),
    .ex_forward_b_sel(ctrl_forward_b_sel_id_out),
    .ex_forward_data_sel(ctrl_forward_data_sel_id_out),
    .id_forward_a_sel(ctrl_id_forward_a_sel),
    .id_forward_b_sel(ctrl_id_forward_b_sel)
  );



  wire [INST_WIDTH - 1:0] inst_ex_in;
  wire [DMEM_DWIDTH - 1:0] rs1_ex_in, rs2_ex_in;
  wire [DMEM_DWIDTH - 1:0] utype_rs1_ex_in;
  wire [DMEM_DWIDTH - 1:0] imm_ex_in;
  wire [PC_WIDTH - 1:0] pc_ex_in;

  wire ctrl_mem_we_ex_in;
  wire ctrl_mem_re_ex_in;
  wire [1:0] ctrl_mem_to_reg_ex_in;
  wire [1:0] ctrl_alu_op_ex_in;
  wire [1:0] ctrl_alu_src_a_ex_in, ctrl_alu_src_b_ex_in;
  wire ctrl_forward_a_sel_ex_in, ctrl_forward_b_sel_ex_in;
  wire ctrl_forward_data_sel_ex_in;

  wire ctrl_csr_we_ex_in;
  wire ctrl_csr_rd_ex_in;
  wire [11:0] csr_addr_ex_in;
  wire [2:0] csr_func_ex_in;


  assign pc_new_if_in = pc_new_id_out;

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_src_a (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_src_a_id_out),
    .q  (ctrl_alu_src_a_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_src_b (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_src_b_id_out),
    .q  (ctrl_alu_src_b_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_reg_we (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_reg_we_id_out),
    .q  (ctrl_reg_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_mem_write (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_write_id_out),
    .q  (ctrl_mem_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_mem_read (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_read_id_out),
    .q  (ctrl_mem_re_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_mem_to_reg (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_to_reg_id_out),
    .q  (ctrl_mem_to_reg_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_op (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_op_id_out),
    .q  (ctrl_alu_op_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_a_sel (
    .clk(clk),
    .d  (ctrl_forward_a_sel_id_out),
    .q  (ctrl_forward_a_sel_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_b_sel (
    .clk(clk),
    .d  (ctrl_forward_b_sel_id_out),
    .q  (ctrl_forward_b_sel_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_data_sel (
    .clk(clk),
    .d  (ctrl_forward_data_sel_id_out),
    .q  (ctrl_forward_data_sel_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_csr_we (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_csr_we_id_out),
    .q  (ctrl_csr_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_csr_rd (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_csr_rd_id_out),
    .q  (ctrl_csr_rd_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_rs1 (
    .clk(clk),
    .rst(rst),
    .d  (rs1_id_out),
    .q  (rs1_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_rs2 (
    .clk(clk),
    .rst(rst),
    .d  (rs2_id_out),
    .q  (rs2_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_utype_rs1 (
    .clk(clk),
    .rst(rst),
    .d  (utype_rs1_id_out),
    .q  (utype_rs1_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_imm (
    .clk(clk),
    .rst(rst),
    .d  (imm_id_out),
    .q  (imm_ex_in)
  );

  REGISTER_R #(
    .N(INST_WIDTH)
  ) id_ex_inst (
    .clk(clk),
    .rst(rst),
    .d  (inst_id_in),
    .q  (inst_ex_in)
  );

  REGISTER_R #(
    .N(12)
  ) id_ex_csr_addr (
    .clk(clk),
    .rst(rst),
    .d  (csr_addr_id_out),
    .q  (csr_addr_ex_in)
  );

  REGISTER_R #(
    .N(3)
  ) id_ex_csr_func (
    .clk(clk),
    .rst(rst),
    .d  (csr_func_id_out),
    .q  (csr_func_ex_in)
  );

  REGISTER_R #(
    .N(PC_WIDTH)
  ) id_ex_pc (
    .clk(clk),
    .rst(rst),
    .d  (pc_id_out),
    .q  (pc_ex_in)
  );



  wire [3:0] alu_func;
  wire [DMEM_DWIDTH - 1:0] alu_out, alu_ex_out, alu_out_ex_sel_out;
  wire [DMEM_DWIDTH - 1:0] mem_ex_out;
  wire [DMEM_DWIDTH - 1:0] csr_data_out, csr_ex_data_out;
  wire [INST_WIDTH - 1:0] mem_mask_inst_in;

  assign alu_func = {inst_ex_in[30], inst_ex_in[14:12]};
  assign addr_rd_ex_in = inst_ex_in[11:7];


  EX #(
    .DWIDTH(DMEM_DWIDTH)
  ) ex (
    .clk(clk),
    .data_rs1(rs1_ex_in),
    .data_rs2(rs2_ex_in),
    .data_imm(imm_ex_in),
    .data_pc(pc_ex_in),
    .ctrl_alu_func(alu_func),
    .ctrl_alu_op(ctrl_alu_op_ex_in),
    .ctrl_alu_src_a(ctrl_alu_src_a_ex_in),
    .ctrl_alu_src_b(ctrl_alu_src_b_ex_in),
    .ctrl_forward_a_sel(ctrl_forward_a_sel_ex_in),
    .ctrl_forward_b_sel(ctrl_forward_b_sel_ex_in),
    .forward_data_in(rd_id_in),
    .alu_out(alu_out),

    .ctrl_csr_we(ctrl_csr_we_ex_in),
    .ctrl_csr_rd(ctrl_csr_rd_ex_in),
    .csr_addr(csr_addr_ex_in),
    .csr_func(csr_func_ex_in),
    .csr_data_out(csr_data_out),
    .csr_orig_data_out(csr)
  );

  assign alu_ex_out_id_in = alu_ex_out;

  wire [3:0] mem_wea;
  reg [DMEM_DWIDTH - 1:0] mem_sel_out;
  wire [1:0] ctrl_mem_to_reg_ex_out;

  assign bios_addrb = alu_out[13:2];
  assign dmem_addra = alu_out[15:2];
  assign imem_addra = alu_out[15:2];

  wire [DMEM_DWIDTH - 1:0] mem_gen_din;
  wire [DMEM_DWIDTH - 1:0] mem_din;
  wire [3:0] mem_din_mask;

  assign mem_gen_din = ctrl_forward_data_sel_ex_in ? rd_id_in : rs2_ex_in;

  MEM_DATA_GEN #(
    .DATA_WIDTH(DMEM_DWIDTH)
  ) mem_in_gen (
    .data_in(mem_gen_din),
    .inst_func_in(inst_ex_in[14:12]),
    .byte_addr_in(alu_out[1:0]),
    .data_out(mem_din),
    .wea_out(mem_din_mask)
  );

  localparam MMIO_AWIDTH = 32;

  wire [MMIO_AWIDTH - 1:0] mmio_addr_in;
  wire [DMEM_DWIDTH - 1:0] mmio_data_in, mmio_data_out, mmio_data_ex_out;
  wire mmio_we_in, mmio_re_in;

  wire [DMEM_DWIDTH - 1:0] mmio_cycle_counter_in, mmio_inst_counter_in;
  wire [7:0] mmio_uart_tx_out, mmio_uart_rx_in;
  wire mmio_uart_tx_ready_in, mmio_uart_rx_valid_in;
  wire mmio_uart_tx_valid_out, mmio_uart_rx_ready_out;
  wire mmio_counter_rst_out;


  MMIO #(
    .AWIDTH(MMIO_AWIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) mmio (
    .addr_in(mmio_addr_in),
    .data_in(mmio_data_in),
    .data_uart_rx_in(mmio_uart_rx_in),
    .data_cycle_counter_in(mmio_cycle_counter_in),
    .data_inst_counter_in(mmio_inst_counter_in),
    .ctrl_uart_tx_ready_in(mmio_uart_tx_ready_in),
    .ctrl_uart_rx_valid_in(mmio_uart_rx_valid_in),
    .we_in(mmio_we_in),
    .re_in(mmio_re_in),
    .data_reg_out(mmio_data_out),
    .data_uart_tx_out(mmio_uart_tx_out),
    .ctrl_uart_tx_valid_out(mmio_uart_tx_valid_out),
    .ctrl_uart_rx_ready_out(mmio_uart_rx_ready_out),
    .ctrl_counter_rst_out(mmio_counter_rst_out)
  );

  wire cycle_counter_rst;
  wire [DMEM_DWIDTH - 1:0] cycle_counter_value;

  CYCLE_COUNTER #(
    .DWIDTH(DMEM_DWIDTH)
  ) cycle_counter (
    .clk  (clk),
    .rst  (cycle_counter_rst),
    .cycle(cycle_counter_value)
  );

  assign cycle_counter_rst = rst | mmio_counter_rst_out;

  wire inst_counter_rst;
  wire [6:0] inst_counter_opcode_in;
  wire [DMEM_DWIDTH - 1:0] inst_counter_value;

  INST_COUNTER #(
    .DWIDTH(DMEM_DWIDTH)
  ) inst_counter (
    .clk(clk),
    .rst(inst_counter_rst),
    .opcode(inst_counter_opcode_in),
    .counter_out(inst_counter_value)
  );

  wire [7:0] uart_tx_fifo_enq_data, uart_tx_fifo_deq_data;
  wire uart_tx_fifo_enq_ready, uart_tx_fifo_enq_valid;
  wire uart_tx_fifo_deq_ready, uart_tx_fifo_deq_valid;

  FIFO #(
    .WIDTH(8),
    .LOGDEPTH(5)
  ) uart_tx_fifo (
    .clk(clk),
    .rst(rst),
    .enq_valid(uart_tx_fifo_enq_valid),
    .enq_ready(uart_tx_fifo_enq_ready),
    .enq_data(uart_tx_fifo_enq_data),
    .deq_valid(uart_tx_fifo_deq_valid),
    .deq_ready(uart_tx_fifo_deq_ready),
    .deq_data(uart_tx_fifo_deq_data)
  );

  assign uart_tx_fifo_enq_data = mmio_uart_tx_out;
  assign uart_tx_fifo_enq_valid = mmio_uart_tx_valid_out;
  assign mmio_uart_tx_ready_in = uart_tx_fifo_enq_ready;
  assign uart_tx_data_in = uart_tx_fifo_deq_data;
  assign uart_tx_data_in_valid = uart_tx_fifo_deq_valid;
  assign uart_tx_fifo_deq_ready = uart_tx_data_in_ready;

  wire [7:0] uart_rx_fifo_enq_data, uart_rx_fifo_deq_data;
  wire uart_rx_fifo_enq_ready, uart_rx_fifo_enq_valid;
  wire uart_rx_fifo_deq_ready, uart_rx_fifo_deq_valid;

  FIFO #(
    .WIDTH(8),
    .LOGDEPTH(5)
  ) uart_rx_fifo (
    .clk(clk),
    .rst(rst),
    .enq_valid(uart_rx_fifo_enq_valid),
    .enq_ready(uart_rx_fifo_enq_ready),
    .enq_data(uart_rx_fifo_enq_data),
    .deq_valid(uart_rx_fifo_deq_valid),
    .deq_ready(uart_rx_fifo_deq_ready),
    .deq_data(uart_rx_fifo_deq_data)
  );

  assign uart_rx_fifo_enq_data = uart_rx_data_out;
  assign uart_rx_fifo_enq_valid = uart_rx_data_out_valid;
  assign uart_rx_data_out_ready = uart_rx_fifo_enq_ready;
  assign mmio_uart_rx_in = uart_rx_fifo_deq_data;
  assign mmio_uart_rx_valid_in = uart_rx_fifo_deq_valid;
  assign uart_rx_fifo_deq_ready = mmio_uart_rx_ready_out;

  assign inst_counter_opcode_in = inst_ex_in[6:0];
  assign inst_counter_rst = rst | mmio_counter_rst_out;
  assign mmio_cycle_counter_in = cycle_counter_value;
  assign mmio_inst_counter_in = inst_counter_value;
  assign mmio_addr_in = alu_out;
  assign mmio_data_in = mem_din;
  assign mmio_we_in = ctrl_mem_we_ex_in;
  assign mmio_re_in = ctrl_mem_re_ex_in;


  assign dmem_dina = mem_din;
  assign imem_dina = mem_din;


  assign dmem_wea = ((alu_out[31:28] & 4'b1101) == 4'b0001 && ctrl_mem_we_ex_in == 1'b1) ? mem_din_mask : 4'h0;

  assign imem_wea = (((alu_out[31:29] & 3'b111) == 3'b001) && (pc_ex_in[30] == 1'b1) && (ctrl_mem_we_ex_in == 1'b1)) ? mem_din_mask : 4'h0;


  wire [1:0] mem_mask_byte_addr;
  wire [2:0] mem_mask_inst_func_in;

  REGISTER #(
    .N(INST_WIDTH)
  ) mem_mask_inst_func (
    .clk(clk),
    .d  (inst_ex_in),
    .q  (mem_mask_inst_in)
  );


  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_alu_out (
    .clk(clk),
    .d  (alu_out),
    .q  (alu_ex_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_alu_sel_out (
    .clk(clk),
    .d  (alu_out),
    .q  (alu_out_ex_sel_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_csr_data_out (
    .clk(clk),
    .d  (csr_data_out),
    .q  (csr_ex_data_out)
  );

  REGISTER #(
    .N(2)
  ) ex_id_ctrl_mem_to_reg (
    .clk(clk),
    .d  (ctrl_mem_to_reg_ex_in),
    .q  (ctrl_mem_to_reg_ex_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_mmio_data_out (
    .clk(clk),
    .d  (mmio_data_out),
    .q  (mmio_data_ex_out)
  );

  wire [PC_WIDTH - 1:0] pc_ex_rd_value, pc_ex_out;
  assign pc_ex_rd_value = pc_ex_in + 4;

  REGISTER #(
    .N(PC_WIDTH)
  ) ex_id_pc_reg (
    .clk(clk),
    .d (pc_ex_rd_value),
    .q (pc_ex_out)
  );




  always @(*) begin
    case (alu_ex_out[31:30])
      2'b00: mem_sel_out = dmem_douta;
      2'b01: mem_sel_out = bios_doutb;
      2'b10: mem_sel_out = mmio_data_ex_out;
      2'b11: mem_sel_out = mmio_data_ex_out;
    endcase
  end

  assign mem_mask_byte_addr = alu_ex_out[1:0];
  assign mem_mask_inst_func_in = mem_mask_inst_in[14:12];


  MEM_DATA_MASK #(
    .DATA_WIDTH(DMEM_DWIDTH)
  ) mem_out_mask (
    .data_in(mem_sel_out),
    .inst_func_in(mem_mask_inst_func_in),
    .byte_addr_in(mem_mask_byte_addr),
    .data_out(mem_ex_out)
  );


  always @(*) begin
    case (ctrl_mem_to_reg_ex_out)
      2'b00:   rd_id_in = alu_out_ex_sel_out;
      2'b01:   rd_id_in = csr_ex_data_out;
      2'b10:   rd_id_in = mem_ex_out;
      2'b11:   rd_id_in = pc_ex_out;
    endcase
  end


  REGISTER #(
    .N(1)
  ) ex_id_reg_we (
    .clk(clk),
    .d  (ctrl_reg_we_ex_in),
    .q  (ctrl_reg_we_id_in)
  );

  REGISTER #(
    .N(5)
  ) ex_id_addr_rd (
    .clk(clk),
    .d  (addr_rd_ex_in),
    .q  (addr_rd_id_in)
  );

endmodule
module Riscv151 #(
  parameter CPU_CLOCK_FREQ = 50_000_000,
  parameter RESET_PC       = 32'h4000_0000,
  parameter BAUD_RATE      = 115200,
  parameter BIOS_MIF_HEX   = "bios151v3.mif"
) (
  input clk,
  input rst,
  input FPGA_SERIAL_RX,
  output FPGA_SERIAL_TX,
  output [31:0] csr
);


  localparam BIOS_AWIDTH = 11;
  localparam BIOS_DWIDTH = 32;

  wire [BIOS_AWIDTH-1:0] bios_addra, bios_addrb;
  wire [BIOS_DWIDTH-1:0] bios_douta, bios_doutb;
  wire bios_ena;




  SYNC_ROM_DP #(
    .AWIDTH (BIOS_AWIDTH),
    .DWIDTH (BIOS_DWIDTH),
    .MIF_HEX(BIOS_MIF_HEX)
  ) bios_mem (
    .q0(bios_douta),
    .addr0(bios_addra),
    .en0(bios_ena),

    .q1(bios_doutb),
    .addr1(bios_addrb),
    .en1(1'b1),

    .clk(clk)
  );

  localparam DMEM_AWIDTH = 14;
  localparam DMEM_DWIDTH = 32;

  wire [DMEM_AWIDTH-1:0] dmem_addra;
  wire [DMEM_DWIDTH-1:0] dmem_dina, dmem_douta;
  wire [3:0] dmem_wea;





  SYNC_RAM_WBE #(
    .AWIDTH(DMEM_AWIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) dmem (
    .q(dmem_douta),
    .d(dmem_dina),
    .addr(dmem_addra),
    .wbe(dmem_wea),
    .en(1'b1),
    .clk(clk)
  );


  wire [7:0] uart_rx_data_out;
  wire uart_rx_data_out_valid;
  wire uart_rx_data_out_ready;

  uart_receiver #(
    .CLOCK_FREQ(CPU_CLOCK_FREQ),
    .BAUD_RATE (BAUD_RATE)
  ) uart_rx (
    .clk           (clk),
    .rst           (rst),
    .data_out      (uart_rx_data_out),
    .data_out_valid(uart_rx_data_out_valid),
    .data_out_ready(uart_rx_data_out_ready),
    .serial_in     (FPGA_SERIAL_RX)
  );


  wire [7:0] uart_tx_data_in;
  wire uart_tx_data_in_valid;
  wire uart_tx_data_in_ready;

  uart_transmitter #(
    .CLOCK_FREQ(CPU_CLOCK_FREQ),
    .BAUD_RATE (BAUD_RATE)
  ) uart_tx (
    .clk          (clk),
    .rst          (rst),
    .data_in      (uart_tx_data_in),
    .data_in_valid(uart_tx_data_in_valid),
    .data_in_ready(uart_tx_data_in_ready),
    .serial_out   (FPGA_SERIAL_TX)
  );




  localparam PC_WIDTH = 32;
  localparam INST_WIDTH = 32;

  wire ctrl_pc_src;
  wire [PC_WIDTH - 1:0] pc_new_if_in, pc_if_out;
  wire pc_en;



  PC #(
    .PC_WIDTH(PC_WIDTH),
    .RESET_PC_VAL(RESET_PC)
  ) pc (
    .clk(clk),
    .rst(rst),
    .pc_sel_in(ctrl_pc_src),
    .pc_en(pc_en),
    .pc_new_in(pc_new_if_in),
    .pc_out(pc_if_out)
  );

  localparam IMEM_AWIDTH = 14;
  localparam IMEM_DWIDTH = 32;

  wire [IMEM_AWIDTH-1:0] imem_addra, imem_addrb;
  wire [IMEM_DWIDTH-1:0] imem_douta, imem_doutb;
  wire [IMEM_DWIDTH-1:0] imem_dina, imem_dinb;
  wire [3:0] imem_wea, imem_web;
  wire imem_enb;

  wire [INST_WIDTH - 1:0] inst_if_out;
  wire inst_if_flush;





  SYNC_RAM_DP_WBE #(
    .AWIDTH(IMEM_AWIDTH),
    .DWIDTH(IMEM_DWIDTH)
  ) imem (
    .q0(imem_douta),
    .d0(imem_dina),
    .addr0(imem_addra),
    .wbe0(imem_wea),
    .en0(1'b1),

    .q1(imem_doutb),
    .d1(imem_dinb),
    .addr1(imem_addrb),
    .wbe1(imem_web),
    .en1(imem_enb),

    .clk(clk)
  );

  wire ctrl_imem_en_id_out;

  assign bios_addra = pc_if_out[13:2];
  assign imem_addrb = pc_if_out[15:2];
  assign imem_web = 4'h0;
  assign inst_if_out = inst_if_flush ? 32'b0 : (pc_if_out[30] == 1'b1) ? bios_douta : imem_doutb;

  assign imem_enb = ctrl_imem_en_id_out;
  assign bios_ena = ctrl_imem_en_id_out;



  wire [  PC_WIDTH - 1:0] pc_id_in;
  wire [INST_WIDTH - 1:0] inst_id_in;

  assign inst_id_in = inst_if_out;

  REGISTER_R_CE #(
    .N(PC_WIDTH),
    .INIT(0)
  ) if_id_pc (
    .d  (pc_if_out),
    .q  (pc_id_in),
    .ce (pc_en),
    .clk(clk),
    .rst(rst)
  );

  wire [DMEM_DWIDTH - 1:0] rs1_id_out, rs2_id_out;
  wire [DMEM_DWIDTH - 1:0] utype_rs1_id_out;
  wire [PC_WIDTH - 1:0] pc_branch_id_out, pc_id_out;
  wire [DMEM_DWIDTH - 1:0] imm_id_out;
  wire [INST_WIDTH - 1:0] pc_new_id_out;
  wire ctrl_zero_sel_id_out;
  wire ctrl_pc_src_id_out, ctrl_reg_we_id_out;
  wire ctrl_mem_write_id_out;
  wire ctrl_mem_read_id_out;
  wire ctrl_id_reg_flush_id_out;
  wire [1:0] ctrl_mem_to_reg_id_out;
  wire [1:0] ctrl_alu_op_id_out;
  wire [1:0] ctrl_alu_src_a_id_out, ctrl_alu_src_b_id_out;
  wire ctrl_forward_a_sel_id_out, ctrl_forward_b_sel_id_out;
  wire ctrl_forward_data_sel_id_out;
  wire ctrl_pc_en_id_out;

  wire ctrl_reg_we_id_in;
  reg [DMEM_DWIDTH - 1:0] rd_id_in;
  wire [4:0] addr_rs1_id_in, addr_rs2_id_in, addr_rd_id_in;

  wire ctrl_csr_we_id_out;
  wire ctrl_csr_rd_id_out;
  wire [11:0] csr_addr_id_out;
  wire [2:0] csr_func_id_out;

  wire [DMEM_DWIDTH - 1:0] alu_ex_out_id_in;
  wire ctrl_id_forward_a_sel, ctrl_id_forward_b_sel;
  wire [4:0] addr_rd_ex_in;

  ID #(
    .PC_WIDTH(PC_WIDTH),
    .INST_WIDTH(INST_WIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) id (
    .clk(clk),
    .rst(rst),

    .pc(pc_id_in),
    .addr_rs1(addr_rs1_id_in),
    .addr_rs2(addr_rs2_id_in),
    .addr_rd(addr_rd_id_in),
    .addr_rd_ex_in(addr_rd_ex_in),
    .inst(inst_id_in),
    .reg_we(ctrl_reg_we_id_in),
    .data_rd(rd_id_in),
    .forward_data_in(rd_id_in),
    .forward_a_sel_in(ctrl_id_forward_a_sel),
    .forward_b_sel_in(ctrl_id_forward_b_sel),

    .data_rs1(rs1_id_out),
    .data_rs2(rs2_id_out),
    .data_imm(imm_id_out),
    .data_pc(pc_id_out),
    .branch_pc_new(pc_new_id_out),
    .ctrl_zero_sel(ctrl_zero_sel_id_out),
    .ctrl_alu_op(ctrl_alu_op_id_out),
    .ctrl_pc_src(ctrl_pc_src_id_out),
    .ctrl_reg_we(ctrl_reg_we_id_out),
    .ctrl_alu_src_a(ctrl_alu_src_a_id_out),
    .ctrl_alu_src_b(ctrl_alu_src_b_id_out),
    .ctrl_mem_write(ctrl_mem_write_id_out),
    .ctrl_mem_read(ctrl_mem_read_id_out),
    .ctrl_mem_to_reg(ctrl_mem_to_reg_id_out),
    .ctrl_pc_en(ctrl_pc_en_id_out),
    .ctrl_imem_en(ctrl_imem_en_id_out),

    .ctrl_id_reg_flush(ctrl_id_reg_flush_id_out),

    .ctrl_csr_we(ctrl_csr_we_id_out),
    .ctrl_csr_rd(ctrl_csr_rd_id_out),
    .csr_addr(csr_addr_id_out),
    .csr_func(csr_func_id_out)
  );


  assign ctrl_pc_src = ctrl_pc_src_id_out;
  assign pc_en = ctrl_pc_en_id_out;

  assign inst_if_flush = ctrl_id_reg_flush_id_out;


  wire ctrl_reg_we_ex_in;
  wire [6:0] opcode_id_in;

  assign opcode_id_in = inst_id_in[6:0];

  assign addr_rs1_id_in = opcode_id_in == `OPC_LUI ? 5'd0 : inst_id_in[19:15];
  assign addr_rs2_id_in = inst_id_in[24:20];

  FORWARD #(
    .DWIDTH(DMEM_DWIDTH)
  ) forward (
    .rs1_addr_id(addr_rs1_id_in),
    .rs2_addr_id(addr_rs2_id_in),
    .opcode_id(opcode_id_in),
    .rd_addr_ex_in(addr_rd_ex_in),
    .rd_addr_id_in(addr_rd_id_in),
    .ctrl_reg_we_ex_in(ctrl_reg_we_ex_in),
    .ctrl_reg_we_id_in(ctrl_reg_we_id_in),
    .ex_forward_a_sel(ctrl_forward_a_sel_id_out),
    .ex_forward_b_sel(ctrl_forward_b_sel_id_out),
    .ex_forward_data_sel(ctrl_forward_data_sel_id_out),
    .id_forward_a_sel(ctrl_id_forward_a_sel),
    .id_forward_b_sel(ctrl_id_forward_b_sel)
  );



  wire [INST_WIDTH - 1:0] inst_ex_in;
  wire [DMEM_DWIDTH - 1:0] rs1_ex_in, rs2_ex_in;
  wire [DMEM_DWIDTH - 1:0] utype_rs1_ex_in;
  wire [DMEM_DWIDTH - 1:0] imm_ex_in;
  wire [PC_WIDTH - 1:0] pc_ex_in;

  wire ctrl_mem_we_ex_in;
  wire ctrl_mem_re_ex_in;
  wire [1:0] ctrl_mem_to_reg_ex_in;
  wire [1:0] ctrl_alu_op_ex_in;
  wire [1:0] ctrl_alu_src_a_ex_in, ctrl_alu_src_b_ex_in;
  wire ctrl_forward_a_sel_ex_in, ctrl_forward_b_sel_ex_in;
  wire ctrl_forward_data_sel_ex_in;

  wire ctrl_csr_we_ex_in;
  wire ctrl_csr_rd_ex_in;
  wire [11:0] csr_addr_ex_in;
  wire [2:0] csr_func_ex_in;


  assign pc_new_if_in = pc_new_id_out;

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_src_a (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_src_a_id_out),
    .q  (ctrl_alu_src_a_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_src_b (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_src_b_id_out),
    .q  (ctrl_alu_src_b_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_reg_we (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_reg_we_id_out),
    .q  (ctrl_reg_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_mem_write (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_write_id_out),
    .q  (ctrl_mem_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_mem_read (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_read_id_out),
    .q  (ctrl_mem_re_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_mem_to_reg (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_mem_to_reg_id_out),
    .q  (ctrl_mem_to_reg_ex_in)
  );

  REGISTER_R #(
    .N(2),
    .INIT(0)
  ) id_ex_ctrl_alu_op (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_alu_op_id_out),
    .q  (ctrl_alu_op_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_a_sel (
    .clk(clk),
    .d  (ctrl_forward_a_sel_id_out),
    .q  (ctrl_forward_a_sel_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_b_sel (
    .clk(clk),
    .d  (ctrl_forward_b_sel_id_out),
    .q  (ctrl_forward_b_sel_ex_in)
  );

  REGISTER #(
    .N(1)
  ) id_ex_ctrl_forward_data_sel (
    .clk(clk),
    .d  (ctrl_forward_data_sel_id_out),
    .q  (ctrl_forward_data_sel_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_csr_we (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_csr_we_id_out),
    .q  (ctrl_csr_we_ex_in)
  );

  REGISTER_R #(
    .N(1),
    .INIT(0)
  ) id_ex_ctrl_csr_rd (
    .clk(clk),
    .rst(ctrl_zero_sel_id_out | rst),
    .d  (ctrl_csr_rd_id_out),
    .q  (ctrl_csr_rd_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_rs1 (
    .clk(clk),
    .rst(rst),
    .d  (rs1_id_out),
    .q  (rs1_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_rs2 (
    .clk(clk),
    .rst(rst),
    .d  (rs2_id_out),
    .q  (rs2_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_utype_rs1 (
    .clk(clk),
    .rst(rst),
    .d  (utype_rs1_id_out),
    .q  (utype_rs1_ex_in)
  );

  REGISTER_R #(
    .N(DMEM_DWIDTH)
  ) id_ex_imm (
    .clk(clk),
    .rst(rst),
    .d  (imm_id_out),
    .q  (imm_ex_in)
  );

  REGISTER_R #(
    .N(INST_WIDTH)
  ) id_ex_inst (
    .clk(clk),
    .rst(rst),
    .d  (inst_id_in),
    .q  (inst_ex_in)
  );

  REGISTER_R #(
    .N(12)
  ) id_ex_csr_addr (
    .clk(clk),
    .rst(rst),
    .d  (csr_addr_id_out),
    .q  (csr_addr_ex_in)
  );

  REGISTER_R #(
    .N(3)
  ) id_ex_csr_func (
    .clk(clk),
    .rst(rst),
    .d  (csr_func_id_out),
    .q  (csr_func_ex_in)
  );

  REGISTER_R #(
    .N(PC_WIDTH)
  ) id_ex_pc (
    .clk(clk),
    .rst(rst),
    .d  (pc_id_out),
    .q  (pc_ex_in)
  );



  wire [3:0] alu_func;
  wire [DMEM_DWIDTH - 1:0] alu_out, alu_ex_out, alu_out_ex_sel_out;
  wire [DMEM_DWIDTH - 1:0] mem_ex_out;
  wire [DMEM_DWIDTH - 1:0] csr_data_out, csr_ex_data_out;
  wire [INST_WIDTH - 1:0] mem_mask_inst_in;

  assign alu_func = {inst_ex_in[30], inst_ex_in[14:12]};
  assign addr_rd_ex_in = inst_ex_in[11:7];


  EX #(
    .DWIDTH(DMEM_DWIDTH)
  ) ex (
    .clk(clk),
    .data_rs1(rs1_ex_in),
    .data_rs2(rs2_ex_in),
    .data_imm(imm_ex_in),
    .data_pc(pc_ex_in),
    .ctrl_alu_func(alu_func),
    .ctrl_alu_op(ctrl_alu_op_ex_in),
    .ctrl_alu_src_a(ctrl_alu_src_a_ex_in),
    .ctrl_alu_src_b(ctrl_alu_src_b_ex_in),
    .ctrl_forward_a_sel(ctrl_forward_a_sel_ex_in),
    .ctrl_forward_b_sel(ctrl_forward_b_sel_ex_in),
    .forward_data_in(rd_id_in),
    .alu_out(alu_out),

    .ctrl_csr_we(ctrl_csr_we_ex_in),
    .ctrl_csr_rd(ctrl_csr_rd_ex_in),
    .csr_addr(csr_addr_ex_in),
    .csr_func(csr_func_ex_in),
    .csr_data_out(csr_data_out),
    .csr_orig_data_out(csr)
  );

  assign alu_ex_out_id_in = alu_ex_out;

  wire [3:0] mem_wea;
  reg [DMEM_DWIDTH - 1:0] mem_sel_out;
  wire [1:0] ctrl_mem_to_reg_ex_out;

  assign bios_addrb = alu_out[13:2];
  assign dmem_addra = alu_out[15:2];
  assign imem_addra = alu_out[15:2];

  wire [DMEM_DWIDTH - 1:0] mem_gen_din;
  wire [DMEM_DWIDTH - 1:0] mem_din;
  wire [3:0] mem_din_mask;

  assign mem_gen_din = ctrl_forward_data_sel_ex_in ? rd_id_in : rs2_ex_in;

  MEM_DATA_GEN #(
    .DATA_WIDTH(DMEM_DWIDTH)
  ) mem_in_gen (
    .data_in(mem_gen_din),
    .inst_func_in(inst_ex_in[14:12]),
    .byte_addr_in(alu_out[1:0]),
    .data_out(mem_din),
    .wea_out(mem_din_mask)
  );

  localparam MMIO_AWIDTH = 32;

  wire [MMIO_AWIDTH - 1:0] mmio_addr_in;
  wire [DMEM_DWIDTH - 1:0] mmio_data_in, mmio_data_out, mmio_data_ex_out;
  wire mmio_we_in, mmio_re_in;

  wire [DMEM_DWIDTH - 1:0] mmio_cycle_counter_in, mmio_inst_counter_in;
  wire [7:0] mmio_uart_tx_out, mmio_uart_rx_in;
  wire mmio_uart_tx_ready_in, mmio_uart_rx_valid_in;
  wire mmio_uart_tx_valid_out, mmio_uart_rx_ready_out;
  wire mmio_counter_rst_out;


  MMIO #(
    .AWIDTH(MMIO_AWIDTH),
    .DWIDTH(DMEM_DWIDTH)
  ) mmio (
    .addr_in(mmio_addr_in),
    .data_in(mmio_data_in),
    .data_uart_rx_in(mmio_uart_rx_in),
    .data_cycle_counter_in(mmio_cycle_counter_in),
    .data_inst_counter_in(mmio_inst_counter_in),
    .ctrl_uart_tx_ready_in(mmio_uart_tx_ready_in),
    .ctrl_uart_rx_valid_in(mmio_uart_rx_valid_in),
    .we_in(mmio_we_in),
    .re_in(mmio_re_in),
    .data_reg_out(mmio_data_out),
    .data_uart_tx_out(mmio_uart_tx_out),
    .ctrl_uart_tx_valid_out(mmio_uart_tx_valid_out),
    .ctrl_uart_rx_ready_out(mmio_uart_rx_ready_out),
    .ctrl_counter_rst_out(mmio_counter_rst_out)
  );

  wire cycle_counter_rst;
  wire [DMEM_DWIDTH - 1:0] cycle_counter_value;

  CYCLE_COUNTER #(
    .DWIDTH(DMEM_DWIDTH)
  ) cycle_counter (
    .clk  (clk),
    .rst  (cycle_counter_rst),
    .cycle(cycle_counter_value)
  );

  assign cycle_counter_rst = rst | mmio_counter_rst_out;

  wire inst_counter_rst;
  wire [6:0] inst_counter_opcode_in;
  wire [DMEM_DWIDTH - 1:0] inst_counter_value;

  INST_COUNTER #(
    .DWIDTH(DMEM_DWIDTH)
  ) inst_counter (
    .clk(clk),
    .rst(inst_counter_rst),
    .opcode(inst_counter_opcode_in),
    .counter_out(inst_counter_value)
  );

  wire [7:0] uart_tx_fifo_enq_data, uart_tx_fifo_deq_data;
  wire uart_tx_fifo_enq_ready, uart_tx_fifo_enq_valid;
  wire uart_tx_fifo_deq_ready, uart_tx_fifo_deq_valid;

  FIFO #(
    .WIDTH(8),
    .LOGDEPTH(5)
  ) uart_tx_fifo (
    .clk(clk),
    .rst(rst),
    .enq_valid(uart_tx_fifo_enq_valid),
    .enq_ready(uart_tx_fifo_enq_ready),
    .enq_data(uart_tx_fifo_enq_data),
    .deq_valid(uart_tx_fifo_deq_valid),
    .deq_ready(uart_tx_fifo_deq_ready),
    .deq_data(uart_tx_fifo_deq_data)
  );

  assign uart_tx_fifo_enq_data = mmio_uart_tx_out;
  assign uart_tx_fifo_enq_valid = mmio_uart_tx_valid_out;
  assign mmio_uart_tx_ready_in = uart_tx_fifo_enq_ready;
  assign uart_tx_data_in = uart_tx_fifo_deq_data;
  assign uart_tx_data_in_valid = uart_tx_fifo_deq_valid;
  assign uart_tx_fifo_deq_ready = uart_tx_data_in_ready;

  wire [7:0] uart_rx_fifo_enq_data, uart_rx_fifo_deq_data;
  wire uart_rx_fifo_enq_ready, uart_rx_fifo_enq_valid;
  wire uart_rx_fifo_deq_ready, uart_rx_fifo_deq_valid;

  FIFO #(
    .WIDTH(8),
    .LOGDEPTH(5)
  ) uart_rx_fifo (
    .clk(clk),
    .rst(rst),
    .enq_valid(uart_rx_fifo_enq_valid),
    .enq_ready(uart_rx_fifo_enq_ready),
    .enq_data(uart_rx_fifo_enq_data),
    .deq_valid(uart_rx_fifo_deq_valid),
    .deq_ready(uart_rx_fifo_deq_ready),
    .deq_data(uart_rx_fifo_deq_data)
  );

  assign uart_rx_fifo_enq_data = uart_rx_data_out;
  assign uart_rx_fifo_enq_valid = uart_rx_data_out_valid;
  assign uart_rx_data_out_ready = uart_rx_fifo_enq_ready;
  assign mmio_uart_rx_in = uart_rx_fifo_deq_data;
  assign mmio_uart_rx_valid_in = uart_rx_fifo_deq_valid;
  assign uart_rx_fifo_deq_ready = mmio_uart_rx_ready_out;

  assign inst_counter_opcode_in = inst_ex_in[6:0];
  assign inst_counter_rst = rst | mmio_counter_rst_out;
  assign mmio_cycle_counter_in = cycle_counter_value;
  assign mmio_inst_counter_in = inst_counter_value;
  assign mmio_addr_in = alu_out;
  assign mmio_data_in = mem_din;
  assign mmio_we_in = ctrl_mem_we_ex_in;
  assign mmio_re_in = ctrl_mem_re_ex_in;


  assign dmem_dina = mem_din;
  assign imem_dina = mem_din;


  assign dmem_wea = ((alu_out[31:28] & 4'b1101) == 4'b0001 && ctrl_mem_we_ex_in == 1'b1) ? mem_din_mask : 4'h0;

  assign imem_wea = (((alu_out[31:29] & 3'b111) == 3'b001) && (pc_ex_in[30] == 1'b1) && (ctrl_mem_we_ex_in == 1'b1)) ? mem_din_mask : 4'h0;


  wire [1:0] mem_mask_byte_addr;
  wire [2:0] mem_mask_inst_func_in;

  REGISTER #(
    .N(INST_WIDTH)
  ) mem_mask_inst_func (
    .clk(clk),
    .d  (inst_ex_in),
    .q  (mem_mask_inst_in)
  );


  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_alu_out (
    .clk(clk),
    .d  (alu_out),
    .q  (alu_ex_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_alu_sel_out (
    .clk(clk),
    .d  (alu_out),
    .q  (alu_out_ex_sel_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_csr_data_out (
    .clk(clk),
    .d  (csr_data_out),
    .q  (csr_ex_data_out)
  );

  REGISTER #(
    .N(2)
  ) ex_id_ctrl_mem_to_reg (
    .clk(clk),
    .d  (ctrl_mem_to_reg_ex_in),
    .q  (ctrl_mem_to_reg_ex_out)
  );

  REGISTER #(
    .N(DMEM_DWIDTH)
  ) ex_id_mmio_data_out (
    .clk(clk),
    .d  (mmio_data_out),
    .q  (mmio_data_ex_out)
  );

  wire [PC_WIDTH - 1:0] pc_ex_rd_value, pc_ex_out;
  assign pc_ex_rd_value = pc_ex_in + 4;

  REGISTER #(
    .N(PC_WIDTH)
  ) ex_id_pc_reg (
    .clk(clk),
    .d (pc_ex_rd_value),
    .q (pc_ex_out)
  );




  always @(*) begin
    case (alu_ex_out[31:30])
      2'b00: mem_sel_out = dmem_douta;
      2'b01: mem_sel_out = bios_doutb;
      2'b10: mem_sel_out = mmio_data_ex_out;
      2'b11: mem_sel_out = mmio_data_ex_out;
    endcase
  end

  assign mem_mask_byte_addr = alu_ex_out[1:0];
  assign mem_mask_inst_func_in = mem_mask_inst_in[14:12];


  MEM_DATA_MASK #(
    .DATA_WIDTH(DMEM_DWIDTH)
  ) mem_out_mask (
    .data_in(mem_sel_out),
    .inst_func_in(mem_mask_inst_func_in),
    .byte_addr_in(mem_mask_byte_addr),
    .data_out(mem_ex_out)
  );


  always @(*) begin
    case (ctrl_mem_to_reg_ex_out)
      2'b00:   rd_id_in = alu_out_ex_sel_out;
      2'b01:   rd_id_in = csr_ex_data_out;
      2'b10:   rd_id_in = mem_ex_out;
      2'b11:   rd_id_in = pc_ex_out;
    endcase
  end


  REGISTER #(
    .N(1)
  ) ex_id_reg_we (
    .clk(clk),
    .d  (ctrl_reg_we_ex_in),
    .q  (ctrl_reg_we_id_in)
  );

  REGISTER #(
    .N(5)
  ) ex_id_addr_rd (
    .clk(clk),
    .d  (addr_rd_ex_in),
    .q  (addr_rd_id_in)
  );

endmodule