blob: 3b7f493a53a1d734e405e2a34ac4bffb6fa89a65 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: Syntacore LLC © 2016-2021
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileContributor: Syntacore LLC
// //////////////////////////////////////////////////////////////////////////
/// Copyright by Syntacore LLC © 2016-2020. See LICENSE for details
/// @file <scr1_pipe_mprf.sv>
/// @brief Multi Port Register File (MPRF)
///
/// Version 1.0 - Dinesh A - Project: yifive
/// added additional stage FF to break timing path
/// additional define SCRC1_MPRF_STAGE added
//////////////////////////////////////////////////////////////////////////////
`include "scr1_arch_description.svh"
`include "scr1_arch_types.svh"
module scr1_pipe_mprf (
// Common
`ifdef SCR1_MPRF_RST_EN
input logic rst_n, // MPRF reset
`endif // SCR1_MPRF_RST_EN
input logic clk, // MPRF clock
// EXU <-> MPRF interface
input logic [`SCR1_MPRF_AWIDTH-1:0] exu2mprf_rs1_addr_i, // MPRF rs1 read address
output logic [`SCR1_XLEN-1:0] mprf2exu_rs1_data_o, // MPRF rs1 read data
input logic [`SCR1_MPRF_AWIDTH-1:0] exu2mprf_rs2_addr_i, // MPRF rs2 read address
output logic [`SCR1_XLEN-1:0] mprf2exu_rs2_data_o, // MPRF rs2 read data
input logic exu2mprf_w_req_i, // MPRF write request
input logic [`SCR1_MPRF_AWIDTH-1:0] exu2mprf_rd_addr_i, // MPRF rd write address
input logic [`SCR1_XLEN-1:0] exu2mprf_rd_data_i // MPRF rd write data
);
//-------------------------------------------------------------------------------
// Local types declaration
//-------------------------------------------------------------------------------
logic wr_req_vd;
logic rs1_addr_vd;
logic rs2_addr_vd;
`ifdef SCRC1_MPRF_STAGE
logic rs1_addr_vd_ff;
logic rs2_addr_vd_ff;
logic rs1_new_data_req;
logic rs2_new_data_req;
logic rs1_new_data_req_ff;
logic rs2_new_data_req_ff;
logic read_new_data_req;
logic [`SCR1_XLEN-1:0] rd_data_ff;
logic [`SCR1_XLEN-1:0] rs1_data_ff;
logic [`SCR1_XLEN-1:0] rs2_data_ff;
`endif
`ifdef SCR1_MPRF_RAM
logic rs1_addr_vd_ff;
logic rs2_addr_vd_ff;
logic rs1_new_data_req;
logic rs2_new_data_req;
logic rs1_new_data_req_ff;
logic rs2_new_data_req_ff;
logic read_new_data_req;
logic [`SCR1_XLEN-1:0] rd_data_ff;
logic [`SCR1_XLEN-1:0] rs1_data_ff;
logic [`SCR1_XLEN-1:0] rs2_data_ff;
// when using RAM, 2 memories are needed because 3 simultaneous independent
// write/read operations can occur
`ifdef SCR1_TRGT_FPGA_INTEL_MAX10
(* ramstyle = "M9K" *) logic [`SCR1_XLEN-1:0] mprf_int [1:`SCR1_MPRF_SIZE-1];
(* ramstyle = "M9K" *) logic [`SCR1_XLEN-1:0] mprf_int2 [1:`SCR1_MPRF_SIZE-1];
`elsif SCR1_TRGT_FPGA_INTEL_ARRIAV
(* ramstyle = "M10K" *) logic [`SCR1_XLEN-1:0] mprf_int [1:`SCR1_MPRF_SIZE-1];
(* ramstyle = "M10K" *) logic [`SCR1_XLEN-1:0] mprf_int2 [1:`SCR1_MPRF_SIZE-1];
`else
logic [`SCR1_XLEN-1:0] mprf_int [1:`SCR1_MPRF_SIZE-1];
logic [`SCR1_XLEN-1:0] mprf_int2 [1:`SCR1_MPRF_SIZE-1];
`endif
`else // distributed logic implementation
logic [`SCR1_XLEN-1:0] mprf_int [1:`SCR1_MPRF_SIZE-1];
`endif
//------------------------------------------------------------------------------
// MPRF control logic
//------------------------------------------------------------------------------
// control signals common for distributed logic and RAM implementations
assign rs1_addr_vd = |exu2mprf_rs1_addr_i;
assign rs2_addr_vd = |exu2mprf_rs2_addr_i;
assign wr_req_vd = exu2mprf_w_req_i & |exu2mprf_rd_addr_i;
// RAM implementation specific control signals
`ifdef SCR1_MPRF_RAM
assign rs1_new_data_req = wr_req_vd & ( exu2mprf_rs1_addr_i == exu2mprf_rd_addr_i );
assign rs2_new_data_req = wr_req_vd & ( exu2mprf_rs2_addr_i == exu2mprf_rd_addr_i );
assign read_new_data_req = rs1_new_data_req | rs2_new_data_req;
always_ff @( posedge clk ) begin
rs1_addr_vd_ff <= rs1_addr_vd;
rs2_addr_vd_ff <= rs2_addr_vd;
rs1_new_data_req_ff <= rs1_new_data_req;
rs2_new_data_req_ff <= rs2_new_data_req;
end
`endif // SCR1_MPRF_RAM
`ifdef SCR1_MPRF_RAM
//-------------------------------------------------------------------------------
// RAM implementation
//-------------------------------------------------------------------------------
// RAM is implemented with 2 simple dual-port memories with sync read operation;
// logic for "write-first" RDW behavior is implemented externally to the embedded
// memory blocks
// bypass new wr_data to the read output if write/read collision occurs
assign mprf2exu_rs1_data_o = ( rs1_new_data_req_ff ) ? rd_data_ff
: (( rs1_addr_vd_ff ) ? rs1_data_ff
: '0 );
assign mprf2exu_rs2_data_o = ( rs2_new_data_req_ff ) ? rd_data_ff
: (( rs2_addr_vd_ff ) ? rs2_data_ff
: '0 );
always_ff @( posedge clk ) begin
if ( read_new_data_req ) begin
rd_data_ff <= exu2mprf_rd_data_i;
end
end
// synchronous read operation
always_ff @( posedge clk ) begin
rs1_data_ff <= mprf_int[exu2mprf_rs1_addr_i];
rs2_data_ff <= mprf_int2[exu2mprf_rs2_addr_i];
end
// write operation
always_ff @( posedge clk ) begin
if ( wr_req_vd ) begin
mprf_int[exu2mprf_rd_addr_i] <= exu2mprf_rd_data_i;
mprf_int2[exu2mprf_rd_addr_i] <= exu2mprf_rd_data_i;
end
end
`else // distributed logic implementation
//------------------------------------------------------------------------------
// distributed logic implementation
//------------------------------------------------------------------------------
// asynchronous read operation
//
`ifdef SCRC1_MPRF_STAGE
assign rs1_new_data_req = wr_req_vd & ( exu2mprf_rs1_addr_i == exu2mprf_rd_addr_i );
assign rs2_new_data_req = wr_req_vd & ( exu2mprf_rs2_addr_i == exu2mprf_rd_addr_i );
assign read_new_data_req = rs1_new_data_req | rs2_new_data_req;
// bypass new wr_data to the read output if write/read collision occurs
assign mprf2exu_rs1_data_o = ( rs1_new_data_req_ff ) ? rd_data_ff
: (( rs1_addr_vd_ff ) ? rs1_data_ff
: '0 );
assign mprf2exu_rs2_data_o = ( rs2_new_data_req_ff ) ? rd_data_ff
: (( rs2_addr_vd_ff ) ? rs2_data_ff
: '0 );
always_ff @( posedge clk ) begin
if ( read_new_data_req ) begin
rd_data_ff <= exu2mprf_rd_data_i;
end
end
always_ff @( posedge clk ) begin
rs1_addr_vd_ff <= rs1_addr_vd;
rs2_addr_vd_ff <= rs2_addr_vd;
rs1_new_data_req_ff <= rs1_new_data_req;
rs2_new_data_req_ff <= rs2_new_data_req;
end
always_ff @( posedge clk ) begin
rs1_data_ff <= mprf_int[exu2mprf_rs1_addr_i];
rs2_data_ff <= mprf_int[exu2mprf_rs2_addr_i];
end
`else
assign mprf2exu_rs1_data_o = ( rs1_addr_vd ) ? mprf_int[exu2mprf_rs1_addr_i] : '0;
assign mprf2exu_rs2_data_o = ( rs2_addr_vd ) ? mprf_int[exu2mprf_rs2_addr_i] : '0;
`endif
// write operation
`ifdef SCR1_MPRF_RST_EN
always_ff @( posedge clk, negedge rst_n ) begin
if ( ~rst_n ) begin
mprf_int <= '{default: '0};
end else if ( wr_req_vd ) begin
mprf_int[exu2mprf_rd_addr_i] <= exu2mprf_rd_data_i;
end
end
`else // ~SCR1_MPRF_RST_EN
always_ff @( posedge clk ) begin
if ( wr_req_vd ) begin
mprf_int[exu2mprf_rd_addr_i] <= exu2mprf_rd_data_i;
end
end
`endif // ~SCR1_MPRF_RST_EN
`endif
`ifdef SCR1_TRGT_SIMULATION
//-------------------------------------------------------------------------------
// Assertion
//-------------------------------------------------------------------------------
`ifdef SCR1_MPRF_RST_EN
SCR1_SVA_MPRF_WRITEX : assert property (
@(negedge clk) disable iff (~rst_n)
exu2mprf_w_req_i |-> !$isunknown({exu2mprf_rd_addr_i, (|exu2mprf_rd_addr_i ? exu2mprf_rd_data_i : `SCR1_XLEN'd0)})
) else $error("MPRF error: unknown values");
`endif // SCR1_MPRF_RST_EN
`endif // SCR1_TRGT_SIMULATION
endmodule : scr1_pipe_mprf