blob: 4f2e3172ffc81d4524f4a4bfec78578a0518779c [file]
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
`timescale 1ns / 1ps
import renode_pkg::renode_runtime, renode_pkg::LogWarning;
module renode_apb3_requester #(int RenodeToCosimIndex = 0) (
ref renode_runtime runtime,
renode_apb3_if bus
);
typedef logic [bus.AddressWidth-1:0] address_t;
typedef logic [bus.DataWidth-1:0] data_t;
// Renaming the bus is a style preference
wire clk;
assign clk = bus.pclk;
logic rst_n;
assign bus.presetn = rst_n;
address_t paddr;
logic pselx;
logic penable;
logic pwrite;
data_t pwdata;
logic pready;
data_t prdata;
logic pslverr;
assign bus.paddr = paddr;
assign bus.pselx = pselx;
assign bus.penable = penable;
assign bus.pwrite = pwrite;
assign bus.pwdata = pwdata;
assign pready = bus.pready;
assign prdata = bus.prdata;
assign pslverr = bus.pslverr;
int unsigned b2b_counter;
address_t write_address;
address_t read_address;
data_t write_data;
logic start_transaction;
logic write_mode;
// Only value of 1 is currently supported
localparam int unsigned Back2BackNum = 1;
always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
write_address = '0;
read_address = '0;
write_data = '0;
start_transaction = '0;
write_mode = '0;
rst_n = 0;
// The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
repeat (2) @(posedge clk);
runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
end
always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
rst_n = 1;
// There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
repeat (2) @(posedge clk);
runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
end
// Internal state
typedef enum {
S_IDLE,
S_SETUP,
S_ACCESS
} state_t;
state_t state = S_IDLE;
//
// Waveform generation
//
always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) begin
integer transaction_width;
if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].read_transaction_address, runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits)) begin
runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
end
transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits);
if (bus.DataWidth > transaction_width) begin
runtime.connection.log(LogWarning,
$sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
end else if (bus.DataWidth < transaction_width) begin
runtime.connection.log(LogWarning,
$sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
end
read_address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address);
write_mode = 1'b0;
start_transaction = 1'b1;
@(posedge clk) start_transaction <= 1'b0;
end
always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) begin
integer transaction_width;
if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].write_transaction_address, runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits)) begin
runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
end
transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits);
if (bus.DataWidth > transaction_width) begin
runtime.connection.log(LogWarning,
$sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
end else if (bus.DataWidth < transaction_width) begin
runtime.connection.log(LogWarning,
$sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
end
write_address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address);
write_data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data);
write_mode = 1'b1;
start_transaction = 1'b1;
@(posedge clk) start_transaction <= 1'b0;
end
state_t next_state;
always_comb begin : proc_next_state
case (state)
S_IDLE: begin
if (start_transaction) begin
next_state = S_SETUP;
end else begin
next_state = S_IDLE;
end
end
S_SETUP: begin
next_state = S_ACCESS;
end
S_ACCESS: begin
if (pready) begin
if (b2b_counter == 0) begin
next_state = S_IDLE;
end else begin
next_state = S_SETUP;
end
end else begin
next_state = S_ACCESS;
end
end
default: begin
next_state = S_IDLE;
end
endcase
end
always_ff @(posedge clk or negedge rst_n) begin
if (rst_n == '0) begin
state <= S_IDLE;
end else begin
state <= next_state;
case (state)
S_IDLE: begin
b2b_counter <= Back2BackNum;
end
S_SETUP: begin
b2b_counter <= b2b_counter - 1;
end
S_ACCESS: begin
if (pready) begin
if (write_mode) begin
runtime.controllers[RenodeToCosimIndex].write_respond(1'b0); // Notify Renode that write is done
end else begin
runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(prdata), 1'b0);
end
end
end
default: begin
b2b_counter <= Back2BackNum;
end
endcase
end
end
always_comb begin : proc_fsm_outputs
case (state)
S_IDLE: begin
paddr = '0;
pselx = '0;
penable = '0;
pwrite = '0;
pwdata = '0;
end
S_SETUP: begin
paddr = write_mode ? write_address : read_address;
pselx = 1'b1;
penable = 1'b0;
pwrite = write_mode;
pwdata = write_mode ? write_data : '0;
end
S_ACCESS: begin
paddr = write_mode ? write_address : read_address;
pselx = 1'b1;
penable = 1'b1;
pwrite = write_mode;
pwdata = write_mode ? write_data : '0;
end
default: begin
paddr = '0;
pselx = '0;
penable = '0;
pwrite = '0;
pwdata = '0;
end
endcase
end
endmodule