blob: 5d7042732d80755109e6f1711e292355db0a0c9a [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_axi_manager (
ref renode_runtime runtime,
renode_axi_if bus
);
import renode_axi_pkg::*;
typedef logic [bus.AddressWidth-1:0] address_t;
typedef logic [bus.DataWidth-1:0] data_t;
typedef logic [bus.StrobeWidth-1:0] strobe_t;
typedef logic [bus.TransactionIdWidth-1:0] transaction_id_t;
wire clk = bus.aclk;
always @(runtime.controller.reset_assert_request) begin
bus.arvalid = 0;
bus.awvalid = 0;
bus.wvalid = 0;
bus.areset_n = 0;
// The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
repeat (2) @(posedge clk);
runtime.controller.reset_assert_respond();
end
always @(runtime.controller.reset_deassert_request) begin
bus.areset_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.controller.reset_deassert_respond();
end
always @(runtime.controller.read_transaction_request) read_transaction();
always @(runtime.controller.write_transaction_request) write_transaction();
task static read_transaction();
bit is_error;
address_t address;
renode_pkg::valid_bits_e valid_bits;
burst_size_t burst_size;
data_t data;
address = address_t'(runtime.controller.read_transaction_address);
valid_bits = runtime.controller.read_transaction_data_bits;
if(!is_access_valid(address, valid_bits)) begin
runtime.controller.read_respond(0, 1);
end else begin
burst_size = bus.valid_bits_to_burst_size(valid_bits);
read(0, address, burst_size, data, is_error);
data = data >> ((address % bus.StrobeWidth) * 8);
runtime.controller.read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
end
endtask
task static write_transaction();
bit is_error;
address_t address;
renode_pkg::valid_bits_e valid_bits;
burst_size_t burst_size;
data_t data;
strobe_t strobe;
address = address_t'(runtime.controller.write_transaction_address);
valid_bits = runtime.controller.write_transaction_data_bits;
if(!is_access_valid(address, valid_bits)) begin
runtime.controller.write_respond(1);
end else begin
burst_size = bus.valid_bits_to_burst_size(valid_bits);
data = data_t'(runtime.controller.write_transaction_data & valid_bits);
strobe = bus.burst_size_to_strobe(burst_size) << (address % bus.StrobeWidth);
data = data << ((address % bus.StrobeWidth) * 8);
write(0, address, burst_size, strobe, data, is_error);
runtime.controller.write_respond(is_error);
end
endtask
function static is_access_valid(address_t address, renode_pkg::valid_bits_e valid_bits);
if(!renode_pkg::is_access_aligned(renode_pkg::address_t'(address), valid_bits)) begin
runtime.connection.log(LogWarning, $sformatf("Unaligned access to 0x%08X is unsupported by AXI Manager. Operation will have no effect.", address));
return 0;
end
if(!bus.are_valid_bits_supported(valid_bits)) begin
runtime.connection.log(LogWarning, $sformatf("This instance of the AXI Manager doesn't support access using the 'b%b mask.", valid_bits));
return 0;
end
return 1;
endfunction
task static read(transaction_id_t id, address_t address, burst_size_t burst_size, output data_t data, output bit is_error);
transaction_id_t response_id;
response_e response;
fork
set_read_address(id, address, burst_size);
get_read_response(data, response_id, response);
join
is_error = check_response(id, response_id, response);
endtask
task static write(transaction_id_t id, address_t address, burst_size_t burst_size, strobe_t strobe, data_t data, output bit is_error);
transaction_id_t response_id;
response_e response;
fork
set_write_address(id, address, burst_size);
set_write_data(data, strobe);
get_write_response(response_id, response);
join
is_error = check_response(id, response_id, response);
endtask
task static set_read_address(transaction_id_t transaction_id, address_t address, burst_size_t burst_size);
bus.arid = transaction_id;
bus.araddr = address;
bus.arsize = burst_size;
// Configure transaction with only one burst.
bus.arlen = 0;
bus.arburst = 0;
bus.arlock = 0;
bus.arprot = 0;
@(posedge clk);
bus.arvalid <= 1;
do @(posedge clk); while (!bus.arready);
bus.arvalid <= 0;
endtask
task static get_read_response(output data_t data, output transaction_id_t transaction_id, output response_e response);
@(posedge clk);
bus.rready <= 1;
do @(posedge clk); while (!bus.rvalid);
data = bus.rdata;
transaction_id = bus.bid;
response = response_e'(bus.bresp);
bus.rready <= 0;
endtask
task static set_write_address(transaction_id_t id, address_t address, burst_size_t burst_size);
bus.awid = id;
bus.awaddr = address;
bus.awsize = burst_size;
// Configure transaction with only one burst.
bus.awlen = 0;
bus.awburst = 0;
bus.awlock = 0;
bus.awprot = 0;
@(posedge clk);
bus.awvalid <= 1;
do @(posedge clk); while (!bus.awready);
bus.awvalid <= 0;
endtask
task static set_write_data(data_t data, strobe_t strobe);
bus.wdata = data;
bus.wstrb = strobe;
bus.wlast = 1;
@(posedge clk);
bus.wvalid <= 1;
do @(posedge clk); while (!bus.wready);
bus.wvalid <= 0;
endtask
task static get_write_response(output transaction_id_t transaction_id, output response_e response);
@(posedge clk);
bus.bready <= 1;
do @(posedge clk); while (!bus.bvalid);
transaction_id = bus.bid;
response = response_e'(bus.bresp);
bus.bready <= 0;
endtask
function automatic bit check_response(transaction_id_t request_id, transaction_id_t response_id, response_t response);
response_e response_enum = response_e'(response);
if (response_id != request_id) begin
runtime.connection.log(LogWarning, $sformatf("Unexpected transaction id in the response ('h%h), expected 'h%h", response_id, request_id));
return 1;
end
if (response_enum != Okay && response_enum != ExclusiveAccessOkay) begin
runtime.connection.log(LogWarning, $sformatf("Response error 'h%h", response));
return 1;
end
return 0;
endfunction
endmodule