Merge branch 'master' into RVFI_DII, a complex merge for the fetch stage!

This commit is contained in:
Jonathan Woodruff
2020-03-18 11:35:59 +00:00
1059 changed files with 1467937 additions and 324026 deletions

View File

@@ -0,0 +1,322 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
package AXI4_Deburster;
// ================================================================
// This package defines a AXI4-slave-to-AXI4-slave conversion module.
// The parameter interface is an AXI4-slave that carries no burst transactions.
// The output interface is an AXI4-slave that carries burst transactions.
// ================================================================
// Bluespec library imports
import Vector :: *;
import FIFOF :: *;
import SpecialFIFOs :: *;
import ConfigReg :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
// ================================================================
// Project imports
import Semi_FIFOF :: *;
import AXI4_Types :: *;
// ================================================================
// The interface for the fabric module
interface AXI4_Deburster_IFC #(numeric type wd_id,
numeric type wd_addr,
numeric type wd_data,
numeric type wd_user);
method Action reset;
// From master
interface AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) from_master;
// To slave
interface AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) to_slave;
endinterface
// ================================================================
// The Deburster module
// The function parameter is an address-decode function, which
// returns (True, slave-port-num) if address is mapped to slave-port-num
// (False, ?) if address is unmapped to any slave port
module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user))
provisos (Add #(a__, 8, wd_addr));
// 0 quiet; 1: display start of burst; 2: display all traffic
Integer cfg_verbosity = 0;
Reg #(Bool) rg_reset <- mkReg (True);
// Transactor facing master
AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)
xactor_from_master <- mkAXI4_Slave_Xactor;
// Transactor facing slave
AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)
xactor_to_slave <- mkAXI4_Master_Xactor;
// On a write-transaction, this register is the W-channel burst beat count
// (0 => start of burst)
Reg #(AXI4_Len) rg_w_beat_count <- mkReg (0);
// On a write-transaction, records awlen for slave
// Size of FIFO should cover slave latency
FIFOF #(AXI4_Len) f_w_awlen <- mkSizedFIFOF (4);
// On a write-transaction, this register is the B-channel burst beat count
// which is the number of individual (non-burst) responses from the
// slave to be combined into a single burst response to the master.
// (0 => ready for next burst)
Reg #(AXI4_Len) rg_b_beat_count <- mkReg (0);
// On a burst write-transaction, all the individual slave responses
// may not have the same 'resp' on the B channel. This register
// remembers the first 'non-okay' resp (if any), to be returned to
// the master in the burst response.
Reg #(AXI4_Resp) rg_b_resp <- mkReg (axi4_resp_okay);
// On a read-transaction, records arlen for slave
// Size of FIFO should cover slave latency
FIFOF #(AXI4_Len) f_r_arlen <- mkSizedFIFOF (4);
// On a read-transaction, this register is the AR-channel burst beat count
// (0 => start of next burst)
Reg #(AXI4_Len) rg_ar_beat_count <- mkReg (0);
// On a read-transaction, this register is the R-channel burst beat count
// (0 => ready for next burst)
Reg #(AXI4_Len) rg_r_beat_count <- mkReg (0);
// ----------------------------------------------------------------
// Compute address for beat
function Bit #(wd_addr) fv_addr_for_beat (Bit #(wd_addr) start_addr,
AXI4_Size axsize,
AXI4_Burst axburst,
AXI4_Len beat_count);
Bit #(wd_addr) addr = start_addr;
if (axburst == axburst_incr)
addr = start_addr + (zeroExtend (beat_count) << pack (axsize));
else if (axburst == axburst_wrap)
addr = start_addr; // TODO: fixup
return addr;
endfunction
// ----------------------------------------------------------------
// RESET
rule rl_reset (rg_reset);
$display ("%0d: %m::AXI4_Deburster.rl_reset", cur_cycle);
xactor_from_master.reset;
xactor_to_slave.reset;
f_w_awlen.clear;
rg_w_beat_count <= 0;
rg_b_beat_count <= 0;
rg_b_resp <= axi4_resp_okay;
f_r_arlen.clear;
rg_ar_beat_count <= 0;
rg_r_beat_count <= 0;
rg_reset <= False;
endrule
// ----------------------------------------------------------------
// BEHAVIOR
// ----------------
// Wr requests (AW and W channels)
rule rl_wr_xaction_master_to_slave;
AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_wr_addr.first;
AXI4_Wr_Data #(wd_data, wd_user) d_in = xactor_from_master.o_wr_data.first;
// Construct output AW item
let a_out = a_in;
a_out.awaddr = fv_addr_for_beat (a_in.awaddr, a_in.awsize, a_in.awburst, rg_w_beat_count);
a_out.awlen = 0;
a_out.awburst = axburst_fixed; // Not necessary when awlen=1, but slave may be finicky
// Set WLAST to true since this is always last beat of outgoing xaction (awlen=1)
let d_out = d_in;
d_out.wlast = True;
// Send to slave
xactor_to_slave.i_wr_addr.enq (a_out);
xactor_to_slave.i_wr_data.enq (d_out);
xactor_from_master.o_wr_data.deq;
// Remember burst length so that individual responses from slave
// can be combined into a single burst response to the master.
if (rg_w_beat_count == 0)
f_w_awlen.enq (a_in.awlen);
if (rg_w_beat_count < a_in.awlen)
rg_w_beat_count <= rg_w_beat_count + 1;
else begin
// Last beat of incoming burst; done with AW item
xactor_from_master.o_wr_addr.deq;
rg_w_beat_count <= 0;
// Simulation-only assertion-check (no action, just display assertion failure)
// Last incoming beat must have WLAST = 1
if (! d_in.wlast) begin
$display ("%0d: ERROR: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s", cur_cycle);
$display (" WLAST not set on last data beat (awlen = %0d)", a_in.awlen);
$display (" ", fshow (d_in));
end
end
// Debugging
if (cfg_verbosity > 0) begin
$display ("%0d: %m::AXI4_Deburster.rl_wr_xaction_master_to_slave: m -> s, beat %0d",
cur_cycle, rg_w_beat_count);
if (rg_w_beat_count == 0)
$display (" a_in : ", fshow (a_in));
if ((rg_w_beat_count == 0) || (cfg_verbosity > 1)) begin
$display (" d_in : ", fshow (d_in));
$display (" a_out: ", fshow (a_out));
$display (" d_out: ", fshow (d_out));
end
end
endrule: rl_wr_xaction_master_to_slave
// ----------------
// Wr responses (B channel): consume responses from slave until the
// last response for a burst, then respond to master. Remember if
// any of them was not an 'okay' response.
rule rl_wr_resp_slave_to_master;
AXI4_Wr_Resp #(wd_id, wd_user) b_in <- pop_o (xactor_to_slave.o_wr_resp);
if (rg_b_beat_count < f_w_awlen.first) begin
// Remember first non-okay response (if any) of a burst in rg_b_resp
if ((rg_b_resp == axi4_resp_okay) && (b_in.bresp != axi4_resp_okay))
rg_b_resp <= b_in.bresp;
// not last beat of burst
rg_b_beat_count <= rg_b_beat_count + 1;
if (cfg_verbosity > 1) begin
$display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
cur_cycle, rg_b_beat_count);
$display (" Consuming and discarding beat %0d", rg_b_beat_count);
$display (" ", fshow (b_in));
end
end
else begin
// Last beat of burst
let b_out = b_in;
if (rg_b_resp != axi4_resp_okay)
b_out.bresp = rg_b_resp;
xactor_from_master.i_wr_resp.enq (b_out);
f_w_awlen.deq;
// Get ready for next burst
rg_b_beat_count <= 0;
rg_b_resp <= axi4_resp_okay;
if (cfg_verbosity > 1) begin
$display ("%0d: %m::AXI4_Deburster.rl_wr_resp_slave_to_master: m <- s, beat %0d",
cur_cycle, rg_b_beat_count);
$display (" b_in: ", fshow (b_in));
$display (" b_out: ", fshow (b_out));
end
end
endrule
// ----------------
// Rd requests (AR channel)
rule rl_rd_xaction_master_to_slave;
AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a_in = xactor_from_master.o_rd_addr.first;
// Compute forwarded request for each beat, and send
let a_out = a_in;
a_out.araddr = fv_addr_for_beat (a_in.araddr, a_in.arsize, a_in.arburst, rg_ar_beat_count);
a_out.arlen = 0;
a_out.arburst = axburst_fixed; // Not necessary when arlen=1, but slave may be finicky
xactor_to_slave.i_rd_addr.enq (a_out);
// On first beat, set up the response count
if (rg_ar_beat_count == 0)
f_r_arlen.enq (a_in.arlen);
if (rg_ar_beat_count < a_in.arlen)
rg_ar_beat_count <= rg_ar_beat_count + 1;
else begin
// Last beat sent; done with AR item
xactor_from_master.o_rd_addr.deq;
rg_ar_beat_count <= 0;
end
// Debugging
if (cfg_verbosity > 0) begin
$display ("%0d: %m::AXI4_Deburster.rl_rd_xaction_master_to_slave: m -> s, beat %0d",
cur_cycle, rg_ar_beat_count);
if (rg_ar_beat_count == 0)
$display (" a_in: ", fshow (a_in));
if ((rg_ar_beat_count == 0) || (cfg_verbosity > 1))
$display (" a_out: ", fshow (a_out));
end
endrule: rl_rd_xaction_master_to_slave
// ----------------
// Rd responses
rule rl_rd_resp_slave_to_master;
AXI4_Rd_Data #(wd_id, wd_data, wd_user) r_in <- pop_o (xactor_to_slave.o_rd_data);
let arlen = f_r_arlen.first;
let r_out = r_in;
if (rg_r_beat_count < arlen) begin
// not last beat of burst
r_out.rlast = False;
rg_r_beat_count <= rg_r_beat_count + 1;
end
else begin
// Last beat of burst
rg_r_beat_count <= 0;
r_out.rlast = True; // should be set already, but override if not
f_r_arlen.deq;
end
xactor_from_master.i_rd_data.enq (r_out);
// Debugging
if (cfg_verbosity > 0) begin
$display ("%0d: %m::AXI4_Deburster.rl_rd_resp_slave_to_master: m <- s, beat %0d",
cur_cycle, rg_r_beat_count);
if ((rg_r_beat_count == 0) || (cfg_verbosity > 1)) begin
$display (" r_in: ", fshow (r_in));
$display (" r_out: ", fshow (r_out));
end
end
endrule: rl_rd_resp_slave_to_master
// ----------------------------------------------------------------
// INTERFACE
method Action reset () if (! rg_reset);
rg_reset <= True;
endmethod
interface from_master = xactor_from_master.axi_side;
interface to_slave = xactor_to_slave .axi_side;
endmodule
// ================================================================
endpackage: AXI4_Deburster

View File

@@ -28,8 +28,8 @@ import AXI4_Types :: *;
// ================================================================
// The interface for the fabric module
interface AXI4_Fabric_IFC #(numeric type num_masters,
numeric type num_slaves,
interface AXI4_Fabric_IFC #(numeric type tn_num_masters,
numeric type tn_num_slaves,
numeric type wd_id,
numeric type wd_addr,
numeric type wd_data,
@@ -38,194 +38,240 @@ interface AXI4_Fabric_IFC #(numeric type num_masters,
method Action set_verbosity (Bit #(4) verbosity);
// From masters
interface Vector #(num_masters, AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_from_masters;
interface Vector #(tn_num_masters, AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_from_masters;
// To slaves
interface Vector #(num_slaves, AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_to_slaves;
interface Vector #(tn_num_slaves, AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_to_slaves;
endinterface
// ================================================================
// The Fabric module
// The function parameter is an address-decode function, which returns
// The function parameter is an address-decode function, which
// returns (True, slave-port-num) if address is mapped to slave-port-num
// (False, ?) if address is unmapped to any port
// (False, ?) if address is unmapped to any slave port
module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(num_slaves)))
module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(tn_num_slaves)))
fn_addr_to_slave_num (Bit #(wd_addr) addr))
(AXI4_Fabric_IFC #(num_masters, num_slaves, wd_id, wd_addr, wd_data, wd_user))
(AXI4_Fabric_IFC #(tn_num_masters, tn_num_slaves, wd_id, wd_addr, wd_data, wd_user))
provisos (Log #(num_masters, log_nm),
Log #(num_slaves, log_ns),
Log #(TAdd #(num_masters, 1), log_nm_plus_1),
Log #(TAdd #(num_slaves, 1), log_ns_plus_1),
Add #(_dummy, TLog #(num_slaves), log_ns_plus_1));
provisos (Log #(tn_num_masters, log_nm),
Log #(tn_num_slaves, log_ns),
Log #(TAdd #(tn_num_slaves, 1), log_ns_plus_1),
Add #(_dummy, TLog #(tn_num_slaves), log_ns_plus_1));
Integer num_masters = valueOf (tn_num_masters);
Integer num_slaves = valueOf (tn_num_slaves);
// 0: quiet; 1: show transactions
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
Reg #(Bool) rg_reset <- mkReg (True);
// Transactors facing masters
Vector #(num_masters, AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
Vector #(tn_num_masters, AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
xactors_from_masters <- replicateM (mkAXI4_Slave_Xactor);
// Transactors facing slaves
Vector #(num_slaves, AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
Vector #(tn_num_slaves, AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user))
xactors_to_slaves <- replicateM (mkAXI4_Master_Xactor);
// FIFOs to keep track of which master originated a transaction, in
// order to route corresponding responses back to that master.
// Legal masters are 0..(num_masters-1)
// The value of 'num_masters' is used for decode errors (no such slave)
// ----------------------------------------------------------------
// Book-keeping to keep track of which master originated a transaction, in
// order to route corresponding responses back to that master, etc.
// Legal slaves are 0..(num_slaves-1)
// The "illegal" value of 'num_slaves' is used for decode errors (no such slave)
// Size of SizedFIFOs is estimated: should cover round-trip latency to slave and back.
Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkSizedFIFOF (8));
Vector #(num_masters, FIFOF #(Bit #(wd_id))) v_f_wr_err_id <- replicateM (mkSizedFIFOF (8));
Vector #(num_masters, FIFOF #(Bit #(wd_user))) v_f_wr_err_user <- replicateM (mkSizedFIFOF (8));
Vector #(num_slaves, FIFOF #(Bit #(log_nm_plus_1))) v_f_wr_mis <- replicateM (mkSizedFIFOF (8));
// ----------------
// Write-transaction book-keeping
Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkSizedFIFOF (8));
Vector #(num_masters, FIFOF #(Bit #(wd_id))) v_f_rd_err_id <- replicateM (mkSizedFIFOF (8));
Vector #(num_masters, FIFOF #(Bit #(wd_user))) v_f_rd_err_user <- replicateM (mkSizedFIFOF (8));
Vector #(num_slaves, FIFOF #(Bit #(log_nm_plus_1))) v_f_rd_mis <- replicateM (mkSizedFIFOF (8));
// On an mi->sj write-transaction, this fifo records sj for master mi
Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkSizedFIFOF (8));
// On an mi->sj write-transaction, this fifo records mi for slave sj
Vector #(tn_num_slaves, FIFOF #(Bit #(log_nm))) v_f_wr_mis <- replicateM (mkSizedFIFOF (8));
// On an mi->sj write-transaction, this fifo records a task (sj, awlen) for W channel
Vector #(tn_num_masters,
FIFOF #(Tuple2 #(Bit #(log_ns_plus_1),
AXI4_Len))) v_f_wd_tasks <- replicateM (mkFIFOF);
// On an mi->sj write-transaction, this register is the W-channel burst beat_count
// (0 => ready for next burst)
Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_wd_beat_count <- replicateM (mkReg (0));
// On a write-transaction to non-exisitent slave, record id and user for error response
Vector #(tn_num_masters,
FIFOF #(Tuple2 #(Bit #(wd_id),
Bit #(wd_user)))) v_f_wr_err_info <- replicateM (mkSizedFIFOF (8));
// ----------------
// Read-transaction book-keeping
// On an mi->sj read-transaction, records sj for master mi
Vector #(tn_num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkSizedFIFOF (8));
// On an mi->sj read-transaction, records (mi,arlen) for slave sj
Vector #(tn_num_slaves,
FIFOF #(Tuple2 #(Bit #(log_nm),
AXI4_Len))) v_f_rd_mis <- replicateM (mkSizedFIFOF (8));
// On an mi->sj read-transaction, this register is the R-channel burst beat_count
// (0 => ready for next burst)
Vector #(tn_num_slaves, Reg #(AXI4_Len)) v_rg_r_beat_count <- replicateM (mkReg (0));
// On a read-transaction to non-exisitent slave, record id and user for error response
Vector #(tn_num_masters,
FIFOF #(Tuple3 #(AXI4_Len,
Bit #(wd_id),
Bit #(wd_user)))) v_f_rd_err_info <- replicateM (mkSizedFIFOF (8));
// On an mi->non-existent-slave read-transaction,
// this register is the R-channel burst beat_count
// (0 => ready for next burst)
Vector #(tn_num_masters, Reg #(AXI4_Len)) v_rg_r_err_beat_count <- replicateM (mkReg (0));
// ----------------------------------------------------------------
// BEHAVIOR
// RESET
rule rl_reset (rg_reset);
$display ("%0d: AXI4_Fabric.rl_reset", cur_cycle);
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) begin
$display ("%0d: %m.rl_reset", cur_cycle);
for (Integer mi = 0; mi < num_masters; mi = mi + 1) begin
xactors_from_masters [mi].reset;
v_f_wr_sjs [mi].clear;
v_f_wr_err_id [mi].clear;
v_f_wr_err_user [mi].clear;
v_f_wd_tasks [mi].clear;
v_rg_wd_beat_count [mi] <= 0;
v_f_wr_err_info [mi].clear;
v_f_rd_sjs [mi].clear;
v_f_rd_err_id [mi].clear;
v_f_rd_err_user [mi].clear;
v_f_rd_err_info [mi].clear;
end
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) begin
for (Integer sj = 0; sj < num_slaves; sj = sj + 1) begin
xactors_to_slaves [sj].reset;
v_f_wr_mis [sj].clear;
v_f_rd_mis [sj].clear;
v_rg_r_beat_count [sj] <= 0;
end
rg_reset <= False;
endrule
// ----------------------------------------------------------------
// Help functions for moving data from masters to slaves
// BEHAVIOR
Integer num_slaves_i = valueOf (num_slaves);
// ----------------------------------------------------------------
// Predicates to check if master I has transaction for slave J
function Bool wr_move_from_mi_to_sj (Integer mi, Integer sj);
function Bool fv_mi_has_wr_for_sj (Integer mi, Integer sj);
let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
return (legal
&& ( (num_slaves_i == 1)
&& ( (num_slaves == 1)
|| (slave_num == fromInteger (sj))));
endfunction
function Bool wr_illegal_sj (Integer mi);
function Bool fv_mi_has_wr_for_none (Integer mi);
let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr;
match { .legal, ._ } = fn_addr_to_slave_num (addr);
return (! legal);
endfunction
function Bool rd_move_from_mi_to_sj (Integer mi, Integer sj);
function Bool fv_mi_has_rd_for_sj (Integer mi, Integer sj);
let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
match { .legal, .slave_num } = fn_addr_to_slave_num (addr);
return (legal
&& ( (num_slaves_i == 1)
&& ( (num_slaves == 1)
|| (slave_num == fromInteger (sj))));
endfunction
function Bool rd_illegal_sj (Integer mi);
function Bool fv_mi_has_rd_for_none (Integer mi);
let addr = xactors_from_masters [mi].o_rd_addr.first.araddr;
match { .legal, ._ } = fn_addr_to_slave_num (addr);
return (! legal);
endfunction
// ----------------
// Wr requests from masters to slaves
// ================================================================
// Wr requests (AW, W and B channels)
// Legal destination slaves
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1)
// Wr requests to legal slaves (AW channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
for (Integer sj = 0; sj < num_slaves; sj = sj + 1)
rule rl_wr_xaction_master_to_slave (wr_move_from_mi_to_sj (mi, sj));
rule rl_wr_xaction_master_to_slave (fv_mi_has_wr_for_sj (mi, sj));
// Move the AW transaction
AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
AXI4_Wr_Data #(wd_id, wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);
xactors_to_slaves [sj].i_wr_addr.enq (a);
xactors_to_slaves [sj].i_wr_data.enq (d);
// Enqueue a task for the W channel
v_f_wd_tasks [mi].enq (tuple2 (fromInteger (sj), a.awlen));
// Book-keeping
v_f_wr_mis [sj].enq (fromInteger (mi));
v_f_wr_sjs [mi].enq (fromInteger (sj));
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: wr master [%0d] -> slave [%0d]", cur_cycle, mi, sj);
$display (" ", fshow (a));
$display (" ", fshow (d));
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_wr_xaction_master_to_slave: m%0d -> s%0d",
cur_cycle, mi, sj);
$display (" ", fshow (a));
end
endrule
// Non-existent destination slaves
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
rule rl_wr_xaction_no_such_slave (wr_illegal_sj (mi));
// Wr requests to non-existent slave (AW channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
rule rl_wr_xaction_no_such_slave (fv_mi_has_wr_for_none (mi));
AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
AXI4_Wr_Data #(wd_id, wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);
AXI4_Wr_Data #(wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);
v_f_wr_sjs [mi].enq (fromInteger (valueOf (num_slaves)));
v_f_wr_err_id [mi].enq (a.awid);
v_f_wr_err_user [mi].enq (a.awuser);
// Special value 'num_slaves' (not a legal sj) means "no such slave"
v_f_wr_sjs [mi].enq (fromInteger (num_slaves));
v_f_wr_err_info [mi].enq (tuple2 (a.awid, a.awuser));
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: wr master [%0d] -> illegal addr", cur_cycle, mi);
// Enqueue a task for the W channel (must consume the write-data burst)
v_f_wd_tasks [mi].enq (tuple2 (fromInteger (num_slaves), a.awlen));
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_wr_xaction_no_such_slave: m%0d -> ?",
cur_cycle, mi);
$display (" ", fshow (a));
end
endrule
// ----------------
// Rd requests from masters to slaves
// Wr data (W channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
// Legal destination slaves
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1)
// Handle W channel burst
// Invariant: v_rg_wd_beat_count == 0 between bursts
// Note: awlen is encoded as 0..255 for burst lengths of 1..256
rule rl_wr_xaction_master_to_slave_data (v_f_wd_tasks [mi].first matches {.sj, .awlen});
AXI4_Wr_Data #(wd_data, wd_user) d <- pop_o (xactors_from_masters [mi].o_wr_data);
rule rl_rd_xaction_master_to_slave (rd_move_from_mi_to_sj (mi, sj));
AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
// If sj is a legal slave, send it the data beat, else drop it.
if (sj < fromInteger (num_slaves))
xactors_to_slaves [sj].i_wr_data.enq (d);
xactors_to_slaves [sj].i_rd_addr.enq (a);
if (v_rg_wd_beat_count [mi] == awlen) begin
// End of burst
v_f_wd_tasks [mi].deq;
v_rg_wd_beat_count [mi] <= 0;
v_f_rd_mis [sj].enq (fromInteger (mi));
v_f_rd_sjs [mi].enq (fromInteger (sj));
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: rd master [%0d] -> slave [%0d]", cur_cycle, mi, sj);
$display (" ", fshow (a));
// Simulation-only assertion-check (no action, just display assertion failure)
// Final beat must have WLAST = 1
// Rely on slave (which should also see this error) to return error response
if (! (d.wlast)) begin
$display ("%0d: %m.rl_wr_xaction_master_to_slave_data: ERROR: m%0d -> s%0d",
cur_cycle, mi, sj);
$display (" WLAST not set on final data beat (awlen = %0d)", awlen);
$display (" ", fshow (d));
end
endrule
end
else
v_rg_wd_beat_count [mi] <= v_rg_wd_beat_count [mi] + 1;
endrule
// Non-existent destination slaves
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
rule rl_rd_xaction_no_such_slave (rd_illegal_sj (mi));
AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
// Wr responses from slaves to masters (B channel)
v_f_rd_sjs [mi].enq (fromInteger (valueOf (num_slaves)));
v_f_rd_err_id [mi].enq (a.arid);
v_f_rd_err_user [mi].enq (a.aruser);
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: rd master [%0d] -> illegal addr", cur_cycle, mi);
$display (" ", fshow (a));
end
endrule
// ----------------
// Wr responses from slaves to masters
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
for (Integer sj = 0; sj < num_slaves; sj = sj + 1)
rule rl_wr_resp_slave_to_master ( (v_f_wr_mis [sj].first == fromInteger (mi))
&& (v_f_wr_sjs [mi].first == fromInteger (sj)));
@@ -235,86 +281,149 @@ module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(num_slaves)))
xactors_from_masters [mi].i_wr_resp.enq (b);
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: wr master [%0d] <- slave [%0d]", cur_cycle, mi, sj);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_wr_resp_slave_to_master: m%0d <- s%0d",
cur_cycle, mi, sj);
$display (" ", fshow (b));
end
endrule
// ----------------
// Wr error responses to masters
// Wr error responses to masters (B channel)
// v_f_wr_sjs [mi].first has value num_slaves (illegal value)
// v_f_wr_err_id [mi].first contains the request's 'id' data
// v_f_wr_err_user [mi].first contains the request's 'user' data
// v_f_wr_err_info [mi].first contains request fields 'awid' and 'awuser'
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (valueOf (num_slaves)));
rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (num_slaves));
v_f_wr_sjs [mi].deq;
v_f_wr_err_id [mi].deq;
v_f_wr_err_user [mi].deq;
v_f_wr_err_info [mi].deq;
let b = AXI4_Wr_Resp {bid: v_f_wr_err_id [mi].first,
match { .awid, .awuser } = v_f_wr_err_info [mi].first;
let b = AXI4_Wr_Resp {bid: awid,
bresp: axi4_resp_decerr,
buser: v_f_wr_err_user [mi].first};
buser: awuser};
xactors_from_masters [mi].i_wr_resp.enq (b);
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: wr master [%0d] <- error", cur_cycle, mi);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_wr_resp_err_to_master: m%0d <- err", cur_cycle, mi);
$display (" ", fshow (b));
end
endrule
// ----------------
// Rd responses from slaves to masters
// ================================================================
// Rd requests (AR and R channels)
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1)
// Rd requests to legal slaves (AR channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
for (Integer sj = 0; sj < num_slaves; sj = sj + 1)
rule rl_rd_resp_slave_to_master ( (v_f_rd_mis [sj].first == fromInteger (mi))
&& (v_f_rd_sjs [mi].first == fromInteger (sj)));
v_f_rd_mis [sj].deq;
v_f_rd_sjs [mi].deq;
AXI4_Rd_Data #(wd_id, wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data);
rule rl_rd_xaction_master_to_slave (fv_mi_has_rd_for_sj (mi, sj));
AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
xactors_from_masters [mi].i_rd_data.enq (r);
xactors_to_slaves [sj].i_rd_addr.enq (a);
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: rd master [%0d] <- slave [%0d]", cur_cycle, mi, sj);
$display (" ", fshow (r));
v_f_rd_mis [sj].enq (tuple2 (fromInteger (mi), a.arlen));
v_f_rd_sjs [mi].enq (fromInteger (sj));
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_rd_xaction_master_to_slave: m%0d -> s%0d",
cur_cycle, mi, sj);
$display (" ", fshow (a));
end
endrule
// ----------------
// Rd error responses to masters
// Rd requests to non-existent slave (AR channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
rule rl_rd_xaction_no_such_slave (fv_mi_has_rd_for_none (mi));
AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
v_f_rd_sjs [mi].enq (fromInteger (num_slaves));
v_f_rd_err_info [mi].enq (tuple3 (a.arlen, a.arid, a.aruser));
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_rd_xaction_no_such_slave: m%0d -> ?",
cur_cycle, mi);
$display (" ", fshow (a));
end
endrule
// Rd responses from slaves to masters (R channel)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
for (Integer sj = 0; sj < num_slaves; sj = sj + 1)
rule rl_rd_resp_slave_to_master (v_f_rd_mis [sj].first matches { .mi2, .arlen }
&&& (mi2 == fromInteger (mi))
&&& (v_f_rd_sjs [mi].first == fromInteger (sj)));
AXI4_Rd_Data #(wd_id, wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data);
if (v_rg_r_beat_count [sj] == arlen) begin
// Final beat of burst
v_f_rd_mis [sj].deq;
v_f_rd_sjs [mi].deq;
v_rg_r_beat_count [sj] <= 0;
// Assertion-check
// Final beat must have RLAST = 1
// If not, and if RRESP is OK, set RRESP to AXI4_RESP_SLVERR
if ((r.rresp == axi4_resp_okay) && (! (r.rlast))) begin
r.rresp = axi4_resp_slverr;
$display ("%0d: %m.rl_rd_resp_slave_to_master: ERROR: m%0d <- s%0d",
cur_cycle, mi, sj);
$display (" RLAST not set on final data beat (arlen = %0d)", arlen);
$display (" ", fshow (r));
end
end
else
v_rg_r_beat_count [sj] <= v_rg_r_beat_count [sj] + 1;
xactors_from_masters [mi].i_rd_data.enq (r);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_rd_resp_slave_to_master: m%0d <- s%0d",
cur_cycle, mi, sj);
$display (" r: ", fshow (r));
end
endrule
// Rd error responses to masters (R channel)
// v_f_rd_sjs [mi].first has value num_slaves (illegal value)
// v_f_rd_err_id [mi].first contains the request's 'id' data
// v_f_rd_err_user [mi].first contains the request's 'user' data
// v_f_rd_err_info [mi].first contains request fields: 'arlen', 'arid', 'aruser'
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
for (Integer mi = 0; mi < num_masters; mi = mi + 1)
rule rl_rd_resp_err_to_master (v_f_rd_sjs [mi].first == fromInteger (valueOf (num_slaves)));
v_f_rd_sjs [mi].deq;
v_f_rd_err_id [mi].deq;
v_f_rd_err_user [mi].deq;
rule rl_rd_resp_err_to_master (v_f_rd_sjs [mi].first == fromInteger (num_slaves));
match { .arlen, .arid, .aruser } = v_f_rd_err_info [mi].first;
Bit #(wd_data) data = 0;
let r = AXI4_Rd_Data {rid: v_f_rd_err_id [mi].first,
let r = AXI4_Rd_Data {rid: arid,
rdata: data,
rresp: axi4_resp_decerr,
rlast: True,
ruser: v_f_rd_err_user [mi].first};
rlast: (v_rg_r_err_beat_count [mi] == arlen),
ruser: aruser};
xactors_from_masters [mi].i_rd_data.enq (r);
if (cfg_verbosity > 1) begin
$display ("%0d: AXI4_Fabric: rd master [%0d] <- error", cur_cycle, mi);
$display (" ", fshow (r));
if (v_rg_r_err_beat_count [mi] == arlen) begin
// Last beat of burst
v_f_rd_sjs [mi].deq;
v_f_rd_err_info [mi].deq;
v_rg_r_err_beat_count [mi] <= 0;
end
else
v_rg_r_err_beat_count [mi] <= v_rg_r_err_beat_count [mi] + 1;
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_rd_resp_err_to_master: m%0d <- err",
cur_cycle, mi);
$display (" r: ", fshow (r));
end
endrule
// ----------------------------------------------------------------
// ================================================================
// INTERFACE
function AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) f1 (Integer j)

View File

@@ -0,0 +1,211 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved.
// Author: Rishiyur S. Nikhil
package AXI4_Mem_Model;
// ================================================================
// A memory-model to be used as a slave on an AXI4 bus.
// Only partical functionality; will be gradually improved over time.
// Current status:
// Address and Data bus widths: 64b
// Bursts: 'fixed' and 'incr' only
// Size: Full 64-bit width reads/writes only
// Strobes: Not yet handled
// memory size: See 'mem_size_word64' definition below
// ================================================================
// Exports
export AXI4_Mem_Model_IFC (..);
export mkAXI4_Mem_Model;
// ================================================================
// Bluespec library imports
import RegFile :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
import Semi_FIFOF :: *;
// ================================================================
// Project imports
import AXI4_Types :: *;
// ================================================================
// INTERFACE
interface AXI4_Mem_Model_IFC #(numeric type wd_id,
numeric type wd_addr,
numeric type wd_data,
numeric type wd_user);
method Action init (Bit #(wd_addr) addr_map_base, Bit #(wd_addr) addr_map_lim);
interface AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) slave;
endinterface
// ================================================================
// IMPLEMENTATION
Integer mem_size_word64 = 'h100_0000; // 16M x 64b words = 128MiB
function Bool fn_addr_ok (Bit #(64) base, Bit #(64) lim, Bit #(64) addr, AXI4_Size size);
let aligned = fn_addr_is_aligned (addr, size);
let in_range = ((base <= addr) && (addr < lim));
return (aligned && in_range);
endfunction
// ----------------
module mkAXI4_Mem_Model (AXI4_Mem_Model_IFC #(wd_id, wd_addr, wd_data, wd_user))
provisos (NumAlias #(wd_addr, 64),
NumAlias #(wd_data, 64));
// 0 = quiet; 1 = show mem transactions
Integer verbosity = 1;
Reg #(Bool) rg_initialized <- mkReg (False);
Reg #(Bit #(wd_addr)) rg_addr_map_base <- mkRegU;
Reg #(Bit #(wd_addr)) rg_addr_map_lim <- mkRegU;
AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) xactor <- mkAXI4_Slave_Xactor;
RegFile #(Bit #(wd_addr), Bit #(wd_data)) rf <- mkRegFile (0, fromInteger (mem_size_word64));
// ================================================================
// Read requests
// TODO: does a bad addr return 'burst-len' err responses or just 1?
Reg #(Bit #(8)) rg_rd_beat <- mkReg (0);
// Recv request on RD_ADDR bus
// Send burst responses on RD_DATA bus
rule rl_read (rg_initialized);
let rd_addr = xactor.o_rd_addr.first;
let rf_index = ((rd_addr.araddr - rg_addr_map_base) >> 3);
if (rd_addr.arburst == axburst_incr)
rf_index = rf_index + zeroExtend (rg_rd_beat);
let last = (rg_rd_beat == rd_addr.arlen);
let addr_ok = fn_addr_ok (rg_addr_map_base, rg_addr_map_lim, rd_addr.araddr, rd_addr.arsize);
let data = (addr_ok ? rf.sub (rf_index) : 0);
AXI4_Rd_Data #(wd_id, wd_data, wd_user)
rd_data = AXI4_Rd_Data {rid: rd_addr.arid,
rdata: data,
rresp: (addr_ok ? axi4_resp_okay : axi4_resp_slverr),
rlast: last,
ruser: rd_addr.aruser};
xactor.i_rd_data.enq (rd_data);
if (last) begin
xactor.o_rd_addr.deq;
rg_rd_beat <= 0;
end
else
rg_rd_beat <= rg_rd_beat + 1;
if (verbosity != 0) begin
$write ("%0d: %m.rl_read: ", cur_cycle);
$write (fshow_Rd_Addr (rd_addr));
$write (fshow_Rd_Data (rd_data));
if (addr_ok)
$display (" beat %0d rf_index 0x%0h", rg_rd_beat, rf_index);
else
$display (" beat 0x%0h BAD ADDR", rg_rd_beat);
end
endrule
// ================================================================
// Write requests
Reg #(Bit #(8)) rg_wr_beat <- mkReg (0);
// Recv request on WR_ADDR bus and burst data on WR_DATA bus,
// send final response on WR_RESP bus
rule rl_write (rg_initialized);
let wr_addr = xactor.o_wr_addr.first;
let wr_data <- pop_o (xactor.o_wr_data);
let rf_index = ((wr_addr.awaddr - rg_addr_map_base) >> 3);
if (wr_addr.awburst == axburst_incr)
rf_index = rf_index + zeroExtend (rg_wr_beat);
let last = (rg_wr_beat == wr_addr.awlen);
let addr_ok = fn_addr_ok (rg_addr_map_base, rg_addr_map_lim, wr_addr.awaddr, wr_addr.awsize);
if (addr_ok)
rf.upd (rf_index, wr_data.wdata);
if (verbosity != 0) begin
$write ("%0d: %m.rl_write: ", cur_cycle);
$write (fshow_Wr_Data (wr_data));
$write (" ", fshow_Wr_Addr (wr_addr));
if (addr_ok)
$display (" beat %0d rf_index %0h", rg_wr_beat, rf_index);
else
$display (" beat %0d BAD ADDR", rg_wr_beat);
end
if (last) begin
AXI4_Wr_Resp #(wd_id, wd_user) wr_resp = ?;
wr_resp = AXI4_Wr_Resp {bid: wr_addr.awid,
bresp: (addr_ok ? axi4_resp_okay : axi4_resp_slverr),
buser: wr_addr.awuser};
xactor.i_wr_resp.enq (wr_resp);
xactor.o_wr_addr.deq;
rg_wr_beat <= 0;
if (verbosity != 0)
$display (" ", fshow_Wr_Resp (wr_resp));
end
else
rg_wr_beat <= rg_wr_beat + 1;
endrule
// ================================================================
// INTERFACE
method Action init (Bit #(wd_addr) addr_map_base, Bit #(wd_addr) addr_map_lim);
if (addr_map_base [2:0] != 3'b0)
$display ("%0d: %m.init: ERROR: unaligned addr_map_base 0x%0h", cur_cycle, addr_map_base);
else if (addr_map_lim [2:0] != 3'b0)
$display ("%0d: %m.init: ERROR: unaligned addr_map_lim 0x%0h", cur_cycle, addr_map_lim);
else if (addr_map_lim <= addr_map_base)
$display ("%0d: %m.init: ERROR: addr_map_base 0x%0h > addr_map_lim 0x%0h",
cur_cycle,
addr_map_base,
addr_map_lim);
else if ((addr_map_lim - addr_map_base) > fromInteger (mem_size_word64 * 8))
$display ("%0d: %m.init: ERROR: mem size (base 0x%0h, lim 0x%0h) > max (0x%0h)",
cur_cycle,
addr_map_base,
addr_map_lim,
fromInteger (mem_size_word64 * 8));
else begin
xactor.reset;
rg_addr_map_base <= addr_map_base;
rg_addr_map_lim <= addr_map_lim;
rg_initialized <= True;
$display ("%0d: %m.init: addr_map_base 0x%0h, addr_map_lim 0x%0h",
cur_cycle,
addr_map_base,
addr_map_lim);
end
endmethod
interface slave = xactor.axi_side;
endmodule
// ================================================================
endpackage

View File

@@ -118,6 +118,20 @@ AXI4_Resp axi4_resp_exokay = 2'b_01;
AXI4_Resp axi4_resp_slverr = 2'b_10;
AXI4_Resp axi4_resp_decerr = 2'b_11;
// ================================================================
// Function to check address-alignment
function Bool fn_addr_is_aligned (Bit #(wd_addr) addr, AXI4_Size size);
return ( (size == axsize_1)
|| ((size == axsize_2) && (addr [0] == 1'b0))
|| ((size == axsize_4) && (addr [1:0] == 2'b0))
|| ((size == axsize_8) && (addr [2:0] == 3'b0))
|| ((size == axsize_16) && (addr [3:0] == 4'b0))
|| ((size == axsize_32) && (addr [4:0] == 5'b0))
|| ((size == axsize_64) && (addr [5:0] == 6'b0))
|| ((size == axsize_128) && (addr [6:0] == 7'b0)));
endfunction
// ================================================================
// These are the signal-level interfaces for an AXI4 master.
// The (*..*) attributes ensure that when bsc compiles this to Verilog,
@@ -150,7 +164,6 @@ interface AXI4_Master_IFC #(numeric type wd_id,
// Wr Data channel
(* always_ready, result="wvalid" *) method Bool m_wvalid; // out
(* always_ready, result="wid" *) method Bit #(wd_id) m_wid; // out
(* always_ready, result="wdata" *) method Bit #(wd_data) m_wdata; // out
(* always_ready, result="wstrb" *) method Bit #(TDiv #(wd_data, 8)) m_wstrb; // out
(* always_ready, result="wlast" *) method Bool m_wlast; // out
@@ -232,7 +245,6 @@ interface AXI4_Slave_IFC #(numeric type wd_id,
// Wr Data channel
(* always_ready, always_enabled, prefix = "" *)
method Action m_wvalid ((* port="wvalid" *) Bool wvalid, // in
(* port="wid" *) Bit #(wd_id) wid, // in
(* port="wdata" *) Bit #(wd_data) wdata, // in
(* port="wstrb" *) Bit #(TDiv #(wd_data,8)) wstrb, // in
(* port="wlast" *) Bool wlast, // in
@@ -306,7 +318,6 @@ instance Connectable #(AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user),
(* fire_when_enabled, no_implicit_conditions *)
rule rl_wr_data_channel;
axis.m_wvalid (axim.m_wvalid,
axim.m_wid,
axim.m_wdata,
axim.m_wstrb,
axim.m_wlast,
@@ -375,7 +386,6 @@ AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)
// Wr Data channel
method Bool m_wvalid = False; // out
method Bit #(wd_id) m_wid = ?; // out
method Bit #(wd_data) m_wdata = ?; // out
method Bit #(TDiv #(wd_data, 8)) m_wstrb = ?; // out
method Bool m_wlast = ?; // out
@@ -446,7 +456,6 @@ AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user)
// Wr Data channel
method Action m_wvalid (Bool wvalid,
Bit #(wd_id) wid,
Bit #(wd_data) wdata,
Bit #(TDiv #(wd_data, 8)) wstrb,
Bool wlast,
@@ -591,13 +600,11 @@ deriving (Bits, FShow);
// Write Data channel
typedef struct {
Bit #(wd_id) wid;
Bit #(wd_data) wdata;
Bit #(TDiv #(wd_data, 8)) wstrb;
Bool wlast;
Bit #(wd_user) wuser;
} AXI4_Wr_Data #(numeric type wd_id,
numeric type wd_data,
} AXI4_Wr_Data #(numeric type wd_data,
numeric type wd_user)
deriving (Bits, FShow);
@@ -643,6 +650,88 @@ typedef struct {
numeric type wd_user)
deriving (Bits, FShow);
// ================================================================
// The following are specialized 'fshow' functions for AXI4 bus
// payloads: the most common fields, and more compact.
function Fmt fshow_AXI4_Size (AXI4_Size size);
Fmt result = ?;
if (size == axsize_1) result = $format ("sz1");
else if (size == axsize_2) result = $format ("sz2");
else if (size == axsize_4) result = $format ("sz4");
else if (size == axsize_8) result = $format ("sz8");
else if (size == axsize_16) result = $format ("sz16");
else if (size == axsize_32) result = $format ("sz32");
else if (size == axsize_64) result = $format ("sz64");
else if (size == axsize_128) result = $format ("sz128");
return result;
endfunction
function Fmt fshow_AXI4_Burst (AXI4_Burst burst);
Fmt result = ?;
if (burst == axburst_fixed) result = $format ("fixed");
else if (burst == axburst_incr) result = $format ("incr");
else if (burst == axburst_wrap) result = $format ("wrap");
else result = $format ("burst:%0d", burst);
return result;
endfunction
function Fmt fshow_AXI4_Resp (AXI4_Resp resp);
Fmt result = ?;
if (resp == axi4_resp_okay) result = $format ("okay");
else if (resp == axi4_resp_exokay) result = $format ("exokay");
else if (resp == axi4_resp_slverr) result = $format ("slverr");
else if (resp == axi4_resp_decerr) result = $format ("decerr");
return result;
endfunction
// ----------------
function Fmt fshow_Wr_Addr (AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) x);
Fmt result = ($format ("{awaddr:%0h,", x.awaddr)
+ $format ("awlen:%0d", x.awlen)
+ $format (",")
+ fshow_AXI4_Size (x.awsize)
+ $format (",")
+ fshow_AXI4_Burst (x.awburst)
+ $format ("}"));
return result;
endfunction
function Fmt fshow_Wr_Data (AXI4_Wr_Data #(wd_data, wd_user) x);
let result = ($format ("{wdata:%0h,wstrb:%0h", x.wdata, x.wstrb)
+ (x.wlast ? $format (",wlast") : $format (",.."))
+ $format ("}"));
return result;
endfunction
function Fmt fshow_Wr_Resp (AXI4_Wr_Resp #(wd_id, wd_user) x);
Fmt result = ($format ("{bresp:")
+ fshow_AXI4_Resp (x.bresp)
+ $format ("}"));
return result;
endfunction
function Fmt fshow_Rd_Addr (AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) x);
Fmt result = ($format ("{araddr:%0h", x.araddr)
+ $format (",arlen:%0d", x.arlen)
+ $format (",")
+ fshow_AXI4_Size (x.arsize)
+ $format (",")
+ fshow_AXI4_Burst (x.arburst)
+ $format ("}"));
return result;
endfunction
function Fmt fshow_Rd_Data (AXI4_Rd_Data #(wd_id, wd_data, wd_user) x);
Fmt result = ($format ("{rresp:")
+ fshow_AXI4_Resp (x.rresp)
+ $format (",rdata:%0h", x.rdata)
+ (x.rlast ? $format (",rlast") : $format (",.."))
+ $format ("}"));
return result;
endfunction
// ================================================================
// AXI4 buffer
@@ -655,7 +744,7 @@ interface AXI4_Server_IFC #(numeric type wd_id,
numeric type wd_user);
interface FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) i_wr_addr;
interface FIFOF_I #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) i_wr_data;
interface FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) i_wr_data;
interface FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) o_wr_resp;
interface FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) i_rd_addr;
@@ -671,7 +760,7 @@ interface AXI4_Client_IFC #(numeric type wd_id,
numeric type wd_user);
interface FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) o_wr_addr;
interface FIFOF_O #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) o_wr_data;
interface FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) o_wr_data;
interface FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) i_wr_resp;
interface FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) o_rd_addr;
@@ -695,7 +784,7 @@ endinterface
module mkAXI4_Buffer (AXI4_Buffer_IFC #(wd_id, wd_addr, wd_data, wd_user));
FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkFIFOF;
FIFOF #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) f_wr_data <- mkFIFOF;
FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkFIFOF;
FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkFIFOF;
FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkFIFOF;
@@ -732,7 +821,7 @@ endmodule
module mkAXI4_Buffer_2 (AXI4_Buffer_IFC #(wd_id, wd_addr, wd_data, wd_user));
FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkMaster_EdgeFIFOF;
FIFOF #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) f_wr_data <- mkMaster_EdgeFIFOF;
FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkMaster_EdgeFIFOF;
FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkSlave_EdgeFIFOF;
FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkMaster_EdgeFIFOF;
@@ -780,7 +869,7 @@ interface AXI4_Master_Xactor_IFC #(numeric type wd_id,
// FIFOF side
interface FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) i_wr_addr;
interface FIFOF_I #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) i_wr_data;
interface FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) i_wr_data;
interface FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) o_wr_resp;
interface FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) i_rd_addr;
@@ -798,7 +887,7 @@ module mkAXI4_Master_Xactor (AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, w
// These FIFOs are guarded on BSV side, unguarded on AXI side
FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (guarded, unguarded);
FIFOF #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) f_wr_data <- mkGFIFOF (guarded, unguarded);
FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkGFIFOF (guarded, unguarded);
FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkGFIFOF (unguarded, guarded);
FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (guarded, unguarded);
@@ -836,7 +925,6 @@ module mkAXI4_Master_Xactor (AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data, w
// Wr Data channel
method Bool m_wvalid = f_wr_data.notEmpty;
method Bit #(wd_id) m_wid = f_wr_data.first.wid;
method Bit #(wd_data) m_wdata = f_wr_data.first.wdata;
method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_wr_data.first.wstrb;
method Bool m_wlast = f_wr_data.first.wlast;
@@ -921,7 +1009,7 @@ module mkAXI4_Master_Xactor_2 (AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data,
Reg #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) rg_wr_addr <- mkRegU;
Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False);
Reg #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) rg_wr_data <- mkRegU;
Reg #(AXI4_Wr_Data #(wd_data, wd_user)) rg_wr_data <- mkRegU;
Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False);
Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU;
@@ -975,7 +1063,6 @@ module mkAXI4_Master_Xactor_2 (AXI4_Master_Xactor_IFC #(wd_id, wd_addr, wd_data,
// Wr Data channel
method Bool m_wvalid = crg_wr_data_full [port_deq];
method Bit #(wd_id) m_wid = rg_wr_data.wid;
method Bit #(wd_data) m_wdata = rg_wr_data.wdata;
method Bit #(TDiv #(wd_data, 8)) m_wstrb = rg_wr_data.wstrb;
method Bool m_wlast = rg_wr_data.wlast;
@@ -1065,7 +1152,7 @@ interface AXI4_Slave_Xactor_IFC #(numeric type wd_id,
// FIFOF side
interface FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) o_wr_addr;
interface FIFOF_O #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) o_wr_data;
interface FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) o_wr_data;
interface FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) i_wr_resp;
interface FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) o_rd_addr;
@@ -1083,7 +1170,7 @@ module mkAXI4_Slave_Xactor (AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_
// These FIFOs are guarded on BSV side, unguarded on AXI side
FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (unguarded, guarded);
FIFOF #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) f_wr_data <- mkGFIFOF (unguarded, guarded);
FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkGFIFOF (unguarded, guarded);
FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkGFIFOF (guarded, unguarded);
FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (unguarded, guarded);
@@ -1135,14 +1222,12 @@ module mkAXI4_Slave_Xactor (AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_
// Wr Data channel
method Action m_wvalid (Bool wvalid,
Bit #(wd_id) wid,
Bit #(wd_data) wdata,
Bit #(TDiv #(wd_data, 8)) wstrb,
Bool wlast,
Bit #(wd_user) wuser);
if (wvalid && f_wr_data.notFull)
f_wr_data.enq (AXI4_Wr_Data {wid: wid,
wdata: wdata,
f_wr_data.enq (AXI4_Wr_Data {wdata: wdata,
wstrb: wstrb,
wlast: wlast,
wuser: wuser});
@@ -1229,7 +1314,7 @@ module mkAXI4_Slave_Xactor_2 (AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, w
Reg #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) rg_wr_addr <- mkRegU;
Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False);
Reg #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) rg_wr_data <- mkRegU;
Reg #(AXI4_Wr_Data #(wd_data, wd_user)) rg_wr_data <- mkRegU;
Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False);
Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU;
@@ -1295,15 +1380,13 @@ module mkAXI4_Slave_Xactor_2 (AXI4_Slave_Xactor_IFC #(wd_id, wd_addr, wd_data, w
// Wr Data channel
method Action m_wvalid (Bool wvalid,
Bit #(wd_id) wid,
Bit #(wd_data) wdata,
Bit #(TDiv #(wd_data, 8)) wstrb,
Bool wlast,
Bit #(wd_user) wuser);
if (wvalid && (! crg_wr_data_full [port_enq])) begin
crg_wr_data_full [port_enq] <= True; // enq
rg_wr_data <= AXI4_Wr_Data {wid: wid,
wdata: wdata,
rg_wr_data <= AXI4_Wr_Data {wdata: wdata,
wstrb: wstrb,
wlast: wlast,
wuser: wuser};

View File

@@ -0,0 +1,73 @@
### -*-Makefile-*-
# Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved
# Makefile for standalone Unit Tester for Deburster (Bluesim only)
.PHONY: all
all: compile simulator
# ================================================================
# Search path for bsc for .bsv files
BSV_ADDL_LIBS=../../../../src_Core/BSV_Additional_Libs
BSC_PATH = -p ..:$(BSV_ADDL_LIBS):+
# ----------------
# Top-level file and module
TOPFILE = Unit_Test_Deburster.bsv
TOPMODULE = mkUnit_Test_Deburster
# ================================================================
# bsc compilation flags
BSC_COMPILATION_FLAGS += \
-keep-fires -aggressive-conditions -no-warn-action-shadowing -no-show-timestamps -check-assert \
-suppress-warnings G0020 \
+RTS -K128M -RTS -show-range-conflict
# ================================================================
# Compile Bluesim intermediate files from BSV sources (needs Bluespec 'bsc' compiler)
TMP_DIRS = -bdir build_dir -simdir build_dir -info-dir build_dir
build_dir:
mkdir -p $@
.PHONY: compile
compile: build_dir
@echo "INFO: Re-compiling BSV sources"
bsc -u -elab -sim $(TMP_DIRS) $(BSC_COMPILATION_FLAGS) $(BSC_PATH) $(TOPFILE)
@echo "INFO: Re-compiled BSV sources"
# ================================================================
# Compile and link Bluesim intermediate files into a Bluesim executable
SIM_EXE_FILE = exe_HW_sim
BSC_C_FLAGS += \
-Xc++ -D_GLIBCXX_USE_CXX11_ABI=0 \
-Xl -v
.PHONY: simulator
simulator:
@echo "INFO: linking bsc-compiled objects into Bluesim executable"
bsc -sim -parallel-sim-link 8 \
$(TMP_DIRS) \
-e $(TOPMODULE) -o ./$(SIM_EXE_FILE) \
$(BSC_C_FLAGS)
@echo "INFO: linked bsc-compiled objects into Bluesim executable"
# ================================================================
.PHONY: clean
clean:
rm -r -f *~ build_dir
.PHONY: full_clean
full_clean: clean
rm -r -f $(SIM_EXE_FILE)* *.log *.vcd
# ================================================================

View File

@@ -0,0 +1,289 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
package Unit_Test_Deburster;
// ================================================================
// Standalone unit tester for AXI4_Deburster.bsv
// ================================================================
// Bluespec library imports
import FIFOF :: *;
import Connectable :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
// ================================================================
// Project imports
import Semi_FIFOF :: *;
import AXI4_Types :: *;
import AXI4_Deburster :: *;
// ================================================================
// Synthesized instance of Deburster
typedef 4 Wd_Id;
typedef 32 Wd_Addr;
typedef 64 Wd_Data;
typedef 10 Wd_User;
typedef AXI4_Deburster_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) AXI4_Deburster_IFC_Inst;
(* synthesize *)
module mkAXI4_Deburster_Inst (AXI4_Deburster_IFC_Inst);
let m <- mkAXI4_Deburster;
return m;
endmodule
// ================================================================
(* synthesize *)
module mkUnit_Test_Deburster (Empty);
AXI4_Deburster_IFC_Inst deburster <- mkAXI4_Deburster_Inst;
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master <- mkAXI4_Master_Xactor;
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave <- mkAXI4_Slave_Xactor;
mkConnection (master.axi_side, deburster.from_master);
mkConnection (deburster.to_slave, slave.axi_side);
Reg #(Bit #(32)) rg_test <- mkReg (20); // Chooses which test to run
FIFOF #(Bit #(8)) f_len <- mkFIFOF;
Reg #(Bit #(8)) rg_beat <- mkReg (0);
Reg #(Bit #(32)) rg_idle_count <- mkReg (0);
// ================================================================
// Help function to create AXI4 channel payloads
function AXI4_Wr_Addr #(Wd_Id, Wd_Addr, Wd_User)
fv_mk_wr_addr (Bit #(Wd_Id) id,
Bit #(Wd_Addr) addr,
Bit #(8) len,
Bit #(2) burst,
Bit #(Wd_User) user);
return AXI4_Wr_Addr {awid: id,
awaddr: addr,
awlen: len,
awsize: axsize_8,
awburst: burst,
awlock: 0,
awcache: 0,
awprot: 0,
awqos: 0,
awregion: 0,
awuser: user};
endfunction
function AXI4_Wr_Data #(Wd_Data, Wd_User)
fv_mk_wr_data (Bit #(Wd_Data) data,
Bit #(Wd_User) user);
Bool last = (rg_beat == f_len.first - 1);
return AXI4_Wr_Data {wdata: data,
wstrb: 'hFF,
wlast: last,
wuser: user};
endfunction
function AXI4_Wr_Resp #(Wd_Id, Wd_User)
fv_mk_wr_resp (AXI4_Wr_Addr #(Wd_Id, Wd_Addr, Wd_User) wa);
return AXI4_Wr_Resp {bid: wa.awid,
bresp: axi4_resp_okay,
buser: wa.awuser};
endfunction
function AXI4_Rd_Addr #(Wd_Id, Wd_Addr, Wd_User)
fv_mk_rd_addr (Bit #(Wd_Id) id,
Bit #(Wd_Addr) addr,
Bit #(8) len,
Bit #(2) burst,
Bit #(Wd_User) user);
return AXI4_Rd_Addr {arid: id,
araddr: addr,
arlen: len,
arsize: axsize_8,
arburst: burst,
arlock: 0,
arcache: 0,
arprot: 0,
arqos: 0,
arregion: 0,
aruser: user};
endfunction
function AXI4_Rd_Data #(Wd_Id, Wd_Data, Wd_User)
fv_mk_rd_data (AXI4_Rd_Addr #(Wd_Id, Wd_Addr, Wd_User) ar);
return AXI4_Rd_Data {rid: ar.arid,
rdata: zeroExtend (ar.araddr + 'h10_000),
rresp: axi4_resp_okay,
rlast: True,
ruser: ar.aruser};
endfunction
// ================================================================
// STIMULUS
Bit #(Wd_Id) id1 = 1;
Bit #(Wd_User) user1 = 1;
// ----------------
// Write tests
rule rl_wr_single (rg_test == 0);
Bit #(8) len = 1;
let wa = fv_mk_wr_addr (id1, 'h1000, (len - 1), axburst_fixed, user1);
master.i_wr_addr.enq (wa);
f_len.enq (len);
rg_idle_count <= 0;
rg_test <= 100;
$display ("%0d: master.rl_wr_single: ", cur_cycle);
$display (" ", fshow (wa));
endrule
rule rl_wr_burst_addr_0 (rg_test == 10);
Bit #(8) len = 2;
let wa = fv_mk_wr_addr (id1, 'h1000, (len - 1), axburst_incr, user1);
master.i_wr_addr.enq (wa);
f_len.enq (len);
rg_idle_count <= 0;
rg_test <= 11;
$display ("%0d: master.rl_wr_burst_addr_0: ", cur_cycle);
$display (" ", fshow (wa));
endrule
rule rl_wr_burst_addr_1 (rg_test == 11);
Bit #(8) len = 4;
let wa = fv_mk_wr_addr (id1, 'h2000, (len - 1), axburst_incr, user1);
master.i_wr_addr.enq (wa);
f_len.enq (len);
rg_idle_count <= 0;
rg_test <= 100;
$display ("%0d: master.rl_wr_burst_addr_1: ", cur_cycle);
$display (" ", fshow (wa));
endrule
rule rl_wr_data;
let data = 'h1_0000 + zeroExtend (rg_beat);
let wd = fv_mk_wr_data (data, user1);
master.i_wr_data.enq (wd);
rg_idle_count <= 0;
if (rg_beat < f_len.first - 1)
rg_beat <= rg_beat + 1;
else begin
rg_beat <= 0;
f_len.deq;
rg_test <= '1;
end
$display ("%0d: master.rl_wr_data: ", cur_cycle);
$display (" ", fshow (wd));
endrule
// ----------------
// Read tests
rule rl_rd_single (rg_test == 2);
let ra = fv_mk_rd_addr (id1, 'h1000, 1, axburst_fixed, user1);
master.i_rd_addr.enq (ra);
rg_idle_count <= 0;
rg_test <= '1;
$display ("%0d: master.rd_single: ", cur_cycle);
$display (" ", fshow (ra));
endrule
rule rl_rd_burst_addr_0 (rg_test == 20);
Bit #(8) len = 2;
let ra = fv_mk_rd_addr (id1, 'h1000, (len - 1), axburst_incr, user1);
master.i_rd_addr.enq (ra);
rg_idle_count <= 0;
rg_test <= 21;
$display ("%0d: master.rl_rd_burst_addr_0: ", cur_cycle);
$display (" ", fshow (ra));
endrule
rule rl_rd_burst_addr_1 (rg_test == 21);
Bit #(8) len = 4;
let ra = fv_mk_rd_addr (id1, 'h2000, (len - 1), axburst_incr, user1);
master.i_rd_addr.enq (ra);
rg_idle_count <= 0;
rg_test <= 100;
$display ("%0d: master.rl_rd_burst_addr_1: ", cur_cycle);
$display (" ", fshow (ra));
endrule
// ================================================================
// Drain and display responses received by master
rule rl_wr_resps;
let wr_resp <- pop_o (master.o_wr_resp);
$display ("%0d: master: ", cur_cycle);
$display (" ", fshow (wr_resp));
rg_idle_count <= 0;
endrule
rule rl_rd_resps;
let rd_resp <- pop_o (master.o_rd_data);
$display ("%0d: master: ", cur_cycle);
$display (" ", fshow (rd_resp));
rg_idle_count <= 0;
endrule
// ================================================================
// Slave: return functional responses
// Note: we should not be receiving any bursts, since we're fronted by the Deburster.
rule rl_slave_IP_model_writes;
$display ("%0d: %m.rl_slave_IP_model_writes: ", cur_cycle);
let wa <- pop_o (slave.o_wr_addr);
let wd <- pop_o (slave.o_wr_data);
let wr = fv_mk_wr_resp (wa);
slave.i_wr_resp.enq (wr);
$display (" ", fshow (wa));
$display (" ", fshow (wd));
$display (" ", fshow (wr));
endrule
rule rl_slave_IP_model_rd_addr;
let ra <- pop_o (slave.o_rd_addr);
slave.i_rd_data.enq (fv_mk_rd_data (ra));
$display ("%0d: slave: ", cur_cycle);
$display (" ", fshow (ra));
endrule
// ================================================================
rule rl_idle_quit;
if (rg_idle_count == 100) begin
$display ("%0d: UnitTest_Deburster: idle; quit", cur_cycle);
$finish (0);
end
else begin
rg_idle_count <= rg_idle_count + 1;
end
endrule
endmodule
// ================================================================
endpackage

View File

@@ -47,7 +47,9 @@ Bits_per_Raw_Mem_Word,
Raw_Mem_Word,
Mem_Controller_IFC (..),
mkMem_Controller;
mkMem_Controller,
status_mem_controller_terminated;
// ================================================================
// BSV library imports
@@ -173,6 +175,11 @@ typedef enum {STATE_POWER_ON_RESET,
} State
deriving (Bits, Eq, FShow);
// ================================================================
// Catch-all status
Integer status_mem_controller_terminated = 1;
// ================================================================
// Interface
@@ -189,6 +196,10 @@ interface Mem_Controller_IFC;
// To raw memory (outside the SoC)
interface MemoryClient #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word) to_raw_mem;
// Catch-all status; return-value can identify the origin (0 = none)
(* always_ready *)
method Bit #(8) status;
// For ISA tests: watch memory writes to <tohost> addr
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
endinterface
@@ -263,6 +274,9 @@ module mkMem_Controller (Mem_Controller_IFC);
Reg #(Bool) rg_watch_tohost <- mkReg (False);
Reg #(Fabric_Addr) rg_tohost_addr <- mkReg ('h_8000_1000);
// Catch-all status
Reg #(Bit #(8)) rg_status <- mkReg (0);
// ================================================================
// BEHAVIOR
@@ -274,6 +288,7 @@ module mkMem_Controller (Mem_Controller_IFC);
slave_xactor.reset;
f_raw_mem_reqs.clear;
f_raw_mem_rsps.clear;
rg_status <= 0;
endaction
endfunction
@@ -535,7 +550,7 @@ module mkMem_Controller (Mem_Controller_IFC);
$display ("PASS");
else
$display ("FAIL %0d", exit_value);
$finish (truncate (exit_value));
rg_status <= fromInteger (status_mem_controller_terminated);
end
endrule
@@ -642,6 +657,11 @@ module mkMem_Controller (Mem_Controller_IFC);
// To raw memory (outside the SoC)
interface to_raw_mem = toGPClient (f_raw_mem_reqs, f_raw_mem_rsps);
// Catch-all status; return-value can identify the origin (0 = none)
method Bit #(8) status;
return rg_status;
endmethod
// For ISA tests: watch memory writes to <tohost> addr
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
rg_watch_tohost <= watch_tohost;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved.
//-
// RVFI_DII modifications:
@@ -31,6 +31,7 @@ import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Memory :: *;
import Clocks :: *;
// ----------------
// BSV additional libs
@@ -42,8 +43,9 @@ import GetPut_Aux :: *;
// Project imports
// Main fabric
import AXI4_Types :: *;
import AXI4_Fabric :: *;
import AXI4_Types :: *;
import AXI4_Fabric :: *;
import AXI4_Deburster :: *;
import Fabric_Defs :: *;
import SoC_Map :: *;
@@ -64,7 +66,8 @@ import Camera_Model :: *;
`endif
`ifdef INCLUDE_ACCEL0
import Accel_AES :: *;
import AXI4_Accel_IFC :: *;
import AXI4_Accel :: *;
`endif
`ifdef INCLUDE_TANDEM_VERIF
@@ -77,16 +80,9 @@ import Types :: *;
`endif
`ifdef INCLUDE_GDB_CONTROL
import External_Control :: *; // Control requests/responses from HSFE
import Debug_Module :: *;
`endif
// ================================================================
// Local types and constants
typedef enum {SOC_START, SOC_RESETTING, SOC_IDLE} SoC_State
deriving (Bits, Eq, FShow);
// ================================================================
// The outermost interface of the SoC
@@ -95,8 +91,11 @@ interface SoC_Top_IFC;
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
`ifdef INCLUDE_GDB_CONTROL
// To external controller (E.g., GDB)
interface Server #(Control_Req, Control_Rsp) server_external_control;
// DMI (Debug Module Interface) facing remote debugger
interface DMI dmi;
// Non-Debug-Module Reset (reset all except DM)
interface Client #(Bool, Bool) ndm_reset_client;
`endif
`ifdef INCLUDE_TANDEM_VERIF
@@ -113,15 +112,29 @@ interface SoC_Top_IFC;
interface Get #(Bit #(8)) get_to_console;
interface Put #(Bit #(8)) put_from_console;
// Catch-all status; return-value can identify the origin (0 = none)
(* always_ready *)
method Bit #(8) status;
// Start CPU execution
// For ISA tests: watch memory writes to <tohost> addr
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
method Action start (Fabric_Addr tohost_addr, Fabric_Addr fromhost_addr);
endinterface
// ================================================================
// Local types and constants
typedef enum {SOC_START,
SOC_RESETTING,
SOC_IDLE} SoC_State
deriving (Bits, Eq, FShow);
// ================================================================
// The module
(* synthesize *)
module mkSoC_Top (SoC_Top_IFC);
module mkSoC_Top #(Reset dm_power_on_reset)
(SoC_Top_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
Reg #(SoC_State) rg_state <- mkReg (SOC_START);
@@ -130,23 +143,35 @@ module mkSoC_Top (SoC_Top_IFC);
SoC_Map_IFC soc_map <- mkSoC_Map;
// Core: CPU + Near_Mem_IO (CLINT) + PLIC + Debug module (optional) + TV (optional)
CoreW_IFC #(N_External_Interrupt_Sources) corew <- mkCoreW;
// The Debug Module has its own RST_N reset signal (which comes
// from outside this module as a paramter)
CoreW_IFC #(N_External_Interrupt_Sources) corew <- mkCoreW (dm_power_on_reset);
// SoC Fabric
Fabric_AXI4_IFC fabric <- mkFabric_AXI4;
// SoC Boot ROM
Boot_ROM_IFC boot_rom <- mkBoot_ROM;
// AXI4 Deburster in front of Boot_ROM
AXI4_Deburster_IFC #(Wd_Id,
Wd_Addr,
Wd_Data,
Wd_User) boot_rom_axi4_deburster <- mkAXI4_Deburster_A;
// SoC Memory
Mem_Controller_IFC mem0_controller <- mkMem_Controller;
// AXI4 Deburster in front of SoC Memory
AXI4_Deburster_IFC #(Wd_Id,
Wd_Addr,
Wd_Data,
Wd_User) mem0_controller_axi4_deburster <- mkAXI4_Deburster_A;
// SoC IPs
UART_IFC uart0 <- mkUART;
`ifdef INCLUDE_ACCEL0
// Accel0 master to fabric
Accel_AES_IFC accel_aes0 <- mkAccel_AES;
AXI4_Accel_IFC accel0 <- mkAXI4_Accel;
`endif
// ----------------
@@ -160,26 +185,28 @@ module mkSoC_Top (SoC_Top_IFC);
mkConnection (corew.cpu_dmem_master, fabric.v_from_masters [dmem_master_num]);
`ifdef INCLUDE_ACCEL0
// accel_aes0 to fabric
mkConnection (accel_aes0.master, fabric.v_from_masters [accel0_master_num]);
// accel to fabric
mkConnection (accel0.master, fabric.v_from_masters [accel0_master_num]);
`endif
// ----------------
// SoC fabric slave connections
// Note: see 'SoC_Map' for 'slave_num' definitions
// Fabric to Boot ROM
mkConnection (fabric.v_to_slaves [boot_rom_slave_num], boot_rom.slave);
// Fabric to Deburster to Boot ROM
mkConnection (fabric.v_to_slaves [boot_rom_slave_num], boot_rom_axi4_deburster.from_master);
mkConnection (boot_rom_axi4_deburster.to_slave, boot_rom.slave);
// Fabric to Mem Controller
mkConnection (fabric.v_to_slaves [mem0_controller_slave_num], mem0_controller.slave);
// Fabric to Deburster to Mem Controller
mkConnection (fabric.v_to_slaves [mem0_controller_slave_num], mem0_controller_axi4_deburster.from_master);
mkConnection (mem0_controller_axi4_deburster.to_slave, mem0_controller.slave);
// Fabric to UART0
mkConnection (fabric.v_to_slaves [uart0_slave_num], uart0.slave);
mkConnection (fabric.v_to_slaves [uart0_slave_num], uart0.slave);
`ifdef INCLUDE_ACCEL0
// Fabric to accel_aes0
mkConnection (fabric.v_to_slaves [accel0_slave_num], accel_aes0.slave);
// Fabric to accel0
mkConnection (fabric.v_to_slaves [accel0_slave_num], accel0.slave);
`endif
`ifdef HTIF_MEMORY
@@ -191,152 +218,89 @@ module mkSoC_Top (SoC_Top_IFC);
// ----------------
// Connect interrupt sources for CPU external interrupt request inputs.
// Reg #(Bool) rg_intr_prev <- mkReg (False); // For debugging only
(* fire_when_enabled, no_implicit_conditions *)
rule rl_connect_external_interrupt_requests;
Bool intr = uart0.intr;
// UART
corew.core_external_interrupt_sources [irq_num_uart0].m_interrupt_req (intr);
Integer last_irq_num = irq_num_uart0;
`ifdef INCLUDE_ACCEL0
Bool intr_accel0 = accel0.interrupt_req;
core.core_external_interrupt_sources [irq_num_accel0].m_interrupt_req (intr_accel0);
last_irq_num = irq_num_accel0;
`endif
// Tie off remaining interrupt request lines (1..N)
for (Integer j = 1; j < valueOf (N_External_Interrupt_Sources); j = j + 1)
for (Integer j = last_irq_num + 1; j < valueOf (N_External_Interrupt_Sources); j = j + 1)
corew.core_external_interrupt_sources [j].m_interrupt_req (False);
// Tie off debugger interrupt
corew.debug_external_interrupt_req (False);
/* For debugging only
if ((! rg_intr_prev) && intr)
$display ("SoC_Top: intr posedge");
else if (rg_intr_prev && (! intr))
$display ("SoC_Top: intr negedge");
rg_intr_prev <= intr;
*/
// Non-maskable interrupt request. [Tie-off; TODO: connect to genuine sources]
corew.nmi_req (False);
endrule
// ================================================================
// RESET BEHAVIOR WITHOUT DEBUG MODULE
// MODULE INITIALIZATIONS
rule rl_reset_start_2 (rg_state == SOC_START);
corew.cpu_reset_server.request.put (?);
mem0_controller.server_reset.request.put (?);
uart0.server_reset.request.put (?);
function Action fa_reset_start_actions;
action
mem0_controller.server_reset.request.put (?);
uart0.server_reset.request.put (?);
fabric.reset;
endaction
endfunction
fabric.reset;
function Action fa_reset_complete_actions;
action
let mem0_controller_rsp <- mem0_controller.server_reset.response.get;
let uart0_rsp <- uart0.server_reset.response.get;
rg_state <= SOC_RESETTING;
// Initialize address maps of slave IPs
boot_rom.set_addr_map (soc_map.m_boot_rom_addr_base,
soc_map.m_boot_rom_addr_lim);
$display ("%0d: SoC_Top. Reset start ...", cur_cycle);
endrule
mem0_controller.set_addr_map (soc_map.m_mem0_controller_addr_base,
soc_map.m_mem0_controller_addr_lim);
// ================================================================
// BEHAVIOR WITH DEBUG MODULE
uart0.set_addr_map (soc_map.m_uart0_addr_base, soc_map.m_uart0_addr_lim);
`ifdef INCLUDE_GDB_CONTROL
// ----------------------------------------------------------------
// External debug requests and responses
FIFOF #(Control_Req) f_external_control_reqs <- mkFIFOF;
FIFOF #(Control_Rsp) f_external_control_rsps <- mkFIFOF;
Control_Req req = f_external_control_reqs.first;
rule rl_handle_external_req_read_request (req.op == external_control_req_op_read_control_fabric);
f_external_control_reqs.deq;
corew.dm_dmi.read_addr (truncate (req.arg1));
if (verbosity != 0) begin
$display ("%0d: SoC_Top.rl_handle_external_req_read_request", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_read_response;
let x <- corew.dm_dmi.read_data;
let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: signExtend (x)};
f_external_control_rsps.enq (rsp);
if (verbosity != 0) begin
$display ("%0d: SoC_Top.rl_handle_external_req_read_response", cur_cycle);
$display (" ", fshow (rsp));
end
endrule
rule rl_handle_external_req_write (req.op == external_control_req_op_write_control_fabric);
f_external_control_reqs.deq;
corew.dm_dmi.write (truncate (req.arg1), truncate (req.arg2));
// let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: 0};
// f_external_control_rsps.enq (rsp);
if (verbosity != 0) begin
$display ("%0d: SoC_Top.rl_handle_external_req_write", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_err ( (req.op != external_control_req_op_read_control_fabric)
&& (req.op != external_control_req_op_write_control_fabric));
f_external_control_reqs.deq;
let rsp = Control_Rsp {status: external_control_rsp_status_err, result: 0};
f_external_control_rsps.enq (rsp);
$display ("%0d: SoC_Top.rl_handle_external_req_err: unknown req.op", cur_cycle);
$display (" ", fshow (req));
endrule
// ----------------------------------------------------------------
// NDM reset (all except Debug Module) request from debug module
rule rl_reset_start (rg_state != SOC_RESETTING);
let req <- corew.dm_ndm_reset_req_get.get;
corew.cpu_reset_server.request.put (?);
mem0_controller.server_reset.request.put (?);
uart0.server_reset.request.put (?);
fabric.reset;
rg_state <= SOC_RESETTING;
$display ("%0d: SoC_Top.rl_reset_start (Debug Module NDM reset, all except debug module) ...",
cur_cycle);
endrule
`ifdef INCLUDE_ACCEL0
accel0.init (fabric_default_id,
soc_map.m_accel0_addr_base,
soc_map.m_accel0_addr_lim);
`endif
rule rl_reset_complete (rg_state == SOC_RESETTING);
let cpu_rsp <- corew.cpu_reset_server.response.get;
let mem0_controller_rsp <- mem0_controller.server_reset.response.get;
let uart0_rsp <- uart0.server_reset.response.get;
if (verbosity != 0) begin
$display (" SoC address map:");
$display (" Boot ROM: 0x%0h .. 0x%0h",
soc_map.m_boot_rom_addr_base,
soc_map.m_boot_rom_addr_lim);
$display (" Mem0 Controller: 0x%0h .. 0x%0h",
soc_map.m_mem0_controller_addr_base,
soc_map.m_mem0_controller_addr_lim);
$display (" UART0: 0x%0h .. 0x%0h",
soc_map.m_uart0_addr_base,
soc_map.m_uart0_addr_lim);
end
endaction
endfunction
// Initialize address maps of slave IPs
boot_rom.set_addr_map (soc_map.m_boot_rom_addr_base,
soc_map.m_boot_rom_addr_lim);
// ----------------
// Initial reset
mem0_controller.set_addr_map (soc_map.m_mem0_controller_addr_base,
soc_map.m_mem0_controller_addr_lim);
rule rl_reset_start_initial (rg_state == SOC_START);
fa_reset_start_actions;
rg_state <= SOC_RESETTING;
uart0.set_addr_map (soc_map.m_uart0_addr_base, soc_map.m_uart0_addr_lim);
$display ("%0d: %m.rl_reset_start_initial ...", cur_cycle);
endrule
rule rl_reset_complete_initial (rg_state == SOC_RESETTING);
fa_reset_complete_actions;
rg_state <= SOC_IDLE;
`ifdef INCLUDE_GDB_CONTROL
$display ("%0d: SoC_Top: NDM reset complete (all except debug module)", cur_cycle);
`else
$display ("%0d: SoC_Top. Reset complete ...", cur_cycle);
`endif
if (verbosity != 0) begin
$display (" SoC address map:");
$display (" Boot ROM: 0x%0h .. 0x%0h",
soc_map.m_boot_rom_addr_base,
soc_map.m_boot_rom_addr_lim);
$display (" Mem0 Controller: 0x%0h .. 0x%0h",
soc_map.m_mem0_controller_addr_base,
soc_map.m_mem0_controller_addr_lim);
$display (" UART0: 0x%0h .. 0x%0h",
soc_map.m_uart0_addr_base,
soc_map.m_uart0_addr_lim);
end
$display ("%0d: %m.rl_reset_complete_initial", cur_cycle);
endrule
// ================================================================
@@ -348,7 +312,11 @@ module mkSoC_Top (SoC_Top_IFC);
// To external controller (E.g., GDB)
`ifdef INCLUDE_GDB_CONTROL
interface server_external_control = toGPServer (f_external_control_reqs, f_external_control_rsps);
// DMI (Debug Module Interface) facing remote debugger
interface DMI dmi = corew.dmi;
// Non-Debug-Module Reset (reset all except DM)
interface Client ndm_reset_client = corew.ndm_reset_client;
`endif
`ifdef INCLUDE_TANDEM_VERIF
@@ -365,16 +333,34 @@ module mkSoC_Top (SoC_Top_IFC);
interface get_to_console = uart0.get_to_console;
interface put_from_console = uart0.put_from_console;
// Catch-all status; return-value can identify the origin (0 = none)
method Bit #(8) status;
return mem0_controller.status;
endmethod
// Start CPU execution
// For ISA tests: watch memory writes to <tohost> addr
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
method Action start (Fabric_Addr tohost_addr, Fabric_Addr fromhost_addr);
Bool watch_tohost = (tohost_addr != 0);
mem0_controller.set_watch_tohost (watch_tohost, tohost_addr);
if (watch_tohost) begin
let fromhost_addr = 'h_8000_1040;
corew.set_htif_addrs (tohost_addr, fromhost_addr);
end
corew.start (tohost_addr, fromhost_addr);
$display ("%0d: %m.method start (tohost %0h, fromhost %0h)",
cur_cycle, tohost_addr, fromhost_addr);
endmethod
endmodule: mkSoC_Top
// ================================================================
// Specialization of parameterized AXI4 Deburster for this SoC.
(* synthesize *)
module mkAXI4_Deburster_A (AXI4_Deburster_IFC #(Wd_Id,
Wd_Addr,
Wd_Data,
Wd_User));
let m <- mkAXI4_Deburster;
return m;
endmodule
// ================================================================
endpackage

View File

@@ -1,24 +1,29 @@
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved
package UART_Model;
// ================================================================
// This package implements a slave IP, a UART model.
//
// This is a very basic (and very incomplete!!) model of a classic
// 16550 UART, just enough to do basic character reads and writes.
// This is a basic (and somewhat incomplete) model of a classic 16550
// UART, enough to do basic character reads and writes, interrupts,
// etc. Just sends/receives the chars into Get/Put interfaces,
// leaving it to external logic to manage actual physical
// tranmit/receive. In particular, this module does nothing about
// clock speed, baud rates, etc.
//
// ----------------
// This slave IP can be attached to fabrics with 32b- or 64b-wide data channels.
// (NOTE: this is the width of the fabric, which can be chosen
// independently of the native width of a CPU master on the
// fabric (such as RV32/RV64 for a RISC-V CPU).
// When attached to 32b-wide fabric, 64-bit locations must be
// read/written in two 32b transaction, once for the lower 32b and
// once for the upper 32b.
// Bus interface width: This slave IP can be attached to fabrics with
// 32b- or 64b-wide data channels. The type parameter 'Wd_Data' in
// Fabric_Defs.bsv specifies this.
//
// Address stride: the 16550 UART's registers are just 1-byte wide.
// As a slave IP in a system, this IP places them at aligned addresses
// with a gap of 4 or 8 bytes. This is controlled by the Integer
// parameter 'address_stride' in this file.
// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
// necessary to satisfy type-checking.
// necessary to satisfy type-checking, to manage these width
// variations.
// ================================================================
export UART_IFC (..), mkUART;
@@ -99,7 +104,7 @@ Bit #(8) uart_lsr_dr = 8'h_01; // Data Ready
Bit #(8) uart_lsr_reset_value = (uart_lsr_temt | uart_lsr_thre);
// ================================================================
// Interface
// THIS MODULE's INTERFACE
interface UART_IFC;
// Reset
@@ -129,19 +134,57 @@ typedef enum {STATE_START,
} Module_State
deriving (Bits, Eq, FShow);
// ----------------
// Split a bus address into (offset in UART, lsbs)
// ================================================================
// Addressing of UART registers
function Tuple3 #(Bit #(2), Bit #(3), Bit #(3)) split_addr (Bit #(64) addr);
// 8-byte stride
Bit #(2) msbs = addr [7:6];
Bit #(3) offset = addr [5:3];
Bit #(3) lsbs = addr [2:0];
// ----------------------------------------------------------------
// UART reg addresses should be at stride 4 or 8.
return tuple3 (msbs, offset, lsbs);
Integer address_stride = 4;
// Integer address_stride = 8;
// ----------------------------------------------------------------
// Split a bus address into (offset, lsbs), based on the address
// stride.
function Tuple2 #(Bit #(64), Bit #(3)) split_addr (Bit #(64) addr);
Bit #(64) offset = ((address_stride == 4) ? (addr >> 2) : (addr >> 3));
Bit #(3) lsbs = ((address_stride == 4) ? { 1'b0, addr [1:0] } : addr [2:0]);
return tuple2 (offset, lsbs);
endfunction
// ----------------------------------------------------------------
// Extract data from AXI4 byte lanes, based on the AXI4 'strobe'
// (byte-enable) bits.
function Bit #(64) fn_extract_AXI4_data (Bit #(64) data, Bit #(8) strb);
Bit #(64) result = 0;
case (strb)
8'b_0000_0001: result = zeroExtend (data [ 7:0]);
8'b_0000_0010: result = zeroExtend (data [15:8]);
8'b_0000_0100: result = zeroExtend (data [23:16]);
8'b_0000_1000: result = zeroExtend (data [31:24]);
8'b_0001_0000: result = zeroExtend (data [39:32]);
8'b_0010_0000: result = zeroExtend (data [47:40]);
8'b_0100_0000: result = zeroExtend (data [55:48]);
8'b_1000_0000: result = zeroExtend (data [63:56]);
8'b_0000_0011: result = zeroExtend (data [15:0]);
8'b_0000_1100: result = zeroExtend (data [31:16]);
8'b_0011_0000: result = zeroExtend (data [47:32]);
8'b_1100_0000: result = zeroExtend (data [63:48]);
8'b_0000_1111: result = zeroExtend (data [31:0]);
8'b_1111_0000: result = zeroExtend (data [63:32]);
8'b_1111_1111: result = zeroExtend (data [63:0]);
endcase
return result;
endfunction
// ================================================================
// THIS MODULE's IMPLEMENTATION
(* synthesize *)
module mkUART (UART_IFC);
@@ -149,6 +192,8 @@ module mkUART (UART_IFC);
Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);
Reg #(Module_State) rg_state <- mkReg (STATE_START);
// These regs represent where this UART is placed in the address space.
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
@@ -156,12 +201,12 @@ module mkUART (UART_IFC);
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
// ----------------
// Connector to fabric
// Connector to AXI4 fabric
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
// ----------------
// character queues to and from the console
// character queues to and from external circuitry for the console
FIFOF #(Bit #(8)) f_from_console <- mkFIFOF;
FIFOF #(Bit #(8)) f_to_console <- mkFIFOF;
@@ -246,61 +291,71 @@ module mkUART (UART_IFC);
let rda <- pop_o (slave_xactor.o_rd_addr);
let byte_addr = rda.araddr - rg_addr_base;
let { msbs, offset, lsbs } = split_addr (zeroExtend (byte_addr));
match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));
Bit #(8) rdata_byte = 0;
AXI4_Resp rresp = axi4_resp_okay;
if (lsbs != 0) begin
$display ("%0d: ERROR: UART.rl_process_rd_req: misaligned addr", cur_cycle);
if ((rda.araddr < rg_addr_base) || (rda.araddr >= rg_addr_lim)) begin
$display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
$display (" UART base addr 0x%0h limit addr 0x%0h", rg_addr_base, rg_addr_lim);
$display (" AXI4 request: ", fshow (rda));
rresp = axi4_resp_decerr;
end
else if (lsbs != 0) begin
$display ("%0d: %m.rl_process_rd_req: ERROR: UART misaligned addr", cur_cycle);
$display (" ", fshow (rda));
rresp = axi4_resp_slverr;
end
else if (msbs != 0) begin
$display ("%0d: ERROR: UART.rl_process_rd_req: unrecognized addr", cur_cycle);
else if (offset [63:3] != 0) begin
$display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
$display (" ", fshow (rda));
rresp = axi4_resp_decerr;
end
// offset 0: RBR
else if ((offset == addr_UART_rbr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
else if ((offset [2:0] == addr_UART_rbr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
// Read an input char
rg_lsr <= (rg_lsr & (~ uart_lsr_dr)); // Reset data-ready
rdata_byte = rg_rbr;
end
// offset 0: DLL
else if ((offset == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
rdata_byte = rg_dll;
// offset 1: IER
else if ((offset == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
rdata_byte = rg_ier;
// offset 1: DLM
else if ((offset == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
rdata_byte = rg_dlm;
// offset 2: IIR (read-only)
else if (offset == addr_UART_iir) rdata_byte = fn_iir();
else if (offset [2:0] == addr_UART_iir) rdata_byte = fn_iir();
// offset 3: LCR
else if (offset == addr_UART_lcr) rdata_byte = { 0, rg_lcr };
else if (offset [2:0] == addr_UART_lcr) rdata_byte = { 0, rg_lcr };
// offset 4: MCR
else if (offset == addr_UART_mcr) rdata_byte = { 0, rg_mcr };
else if (offset [2:0] == addr_UART_mcr) rdata_byte = { 0, rg_mcr };
// offset 5: LSR
else if (offset == addr_UART_lsr) rdata_byte = { 0, rg_lsr };
else if (offset [2:0] == addr_UART_lsr) rdata_byte = { 0, rg_lsr };
// offset 6: MSR
else if (offset == addr_UART_msr) rdata_byte = { 0, rg_msr };
else if (offset [2:0] == addr_UART_msr) rdata_byte = { 0, rg_msr };
// offset 7: SCR
else if (offset == addr_UART_scr) rdata_byte = { 0, rg_scr };
else if (offset [2:0] == addr_UART_scr) rdata_byte = { 0, rg_scr };
else begin
$display ("%0d: ERROR: UART.rl_process_rd_req: unrecognized addr", cur_cycle);
$display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
$display (" ", fshow (rda));
rresp = axi4_resp_decerr;
end
// Send read-response to bus
// Align data byte for AXI4 data bus based on fabric-width
Fabric_Data rdata = zeroExtend (rdata_byte);
if ((valueOf (Wd_Data) == 64) && (byte_addr [2:0] == 3'b100))
rdata = rdata << 32;
// Send read-response to bus
let rdr = AXI4_Rd_Data {rid: rda.arid,
rdata: rdata,
rresp: rresp,
@@ -309,7 +364,7 @@ module mkUART (UART_IFC);
slave_xactor.i_rd_data.enq (rdr);
if (cfg_verbosity > 1) begin
$display ("%0d: UART.rl_process_rd_req", cur_cycle);
$display ("%0d: %m.rl_process_rd_req", cur_cycle);
$display (" ", fshow (rda));
$display (" ", fshow (rdr));
end
@@ -324,59 +379,63 @@ module mkUART (UART_IFC);
Bit #(64) wdata = zeroExtend (wrd.wdata);
Bit #(8) wstrb = zeroExtend (wrd.wstrb);
Bit #(8) data_byte = wdata [7:0];
Bit #(8) data_byte = truncate (fn_extract_AXI4_data (wdata, wstrb));
let byte_addr = wra.awaddr - rg_addr_base;
let { msbs, offset, lsbs } = split_addr (zeroExtend (byte_addr));
match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));
AXI4_Resp bresp = axi4_resp_okay;
if ((lsbs != 0) || (wstrb [0] == 1'b0)) begin
$display ("%0d: ERROR: UART.rl_process_wr_req: misaligned addr", cur_cycle);
if ((wra.awaddr < rg_addr_base) || (wra.awaddr >= rg_addr_lim)) begin
$display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
$display (" UART base addr 0x%0h limit addr 0x%0h", rg_addr_base, rg_addr_lim);
$display (" AXI4 request: ", fshow (wra));
bresp = axi4_resp_decerr;
end
else if (lsbs != 0) begin
$display ("%0d: %m.rl_process_wr_req: ERROR: UART misaligned addr", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
bresp = axi4_resp_slverr;
end
else if (msbs != 0) begin
$display ("%0d: ERROR: UART.rl_process_wr_req: unrecognized addr", cur_cycle);
else if (offset [63:3] != 0) begin
$display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
bresp = axi4_resp_decerr;
end
// offset 0: THR
else if ((offset == addr_UART_thr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
else if ((offset [2:0] == addr_UART_thr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
// Write a char to the serial line
rg_thr <= data_byte;
f_to_console.enq (data_byte);
end
// offset 0: DLL
else if ((offset == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
rg_dll <= data_byte;
// offset 1: IER
else if ((offset == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
rg_ier <= data_byte;
// offset 1: DLM
else if ((offset == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
rg_dlm <= data_byte;
// offset 2: FCR (write-only)
else if (offset == addr_UART_fcr) rg_fcr <= data_byte;
else if (offset [2:0] == addr_UART_fcr) rg_fcr <= data_byte;
// offset 3: LCR
else if (offset == addr_UART_lcr) rg_lcr <= data_byte;
else if (offset [2:0] == addr_UART_lcr) rg_lcr <= data_byte;
// offset 4: MCR
else if (offset == addr_UART_mcr) rg_mcr <= data_byte;
else if (offset [2:0] == addr_UART_mcr) rg_mcr <= data_byte;
// offset 5: LSR
else if (offset == addr_UART_lsr) noAction; // LSR is read-only
else if (offset [2:0] == addr_UART_lsr) noAction; // LSR is read-only
// offset 6: MSR
else if (offset == addr_UART_msr) noAction; // MSR is read-only
else if (offset [2:0] == addr_UART_msr) noAction; // MSR is read-only
// offset 7: SCR
else if (offset == addr_UART_scr) rg_scr <= data_byte;
else if (offset [2:0] == addr_UART_scr) rg_scr <= data_byte;
else begin
$display ("%0d: ERROR: UART.rl_process_wr_req: unrecognized addr", cur_cycle);
$display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
bresp = axi4_resp_decerr;
@@ -389,7 +448,7 @@ module mkUART (UART_IFC);
slave_xactor.i_wr_resp.enq (wrr);
if (cfg_verbosity > 1) begin
$display ("%0d: UART.rl_process_wr_req", cur_cycle);
$display ("%0d: %m.rl_process_wr_req", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
$display (" ", fshow (wrr));
@@ -401,6 +460,8 @@ module mkUART (UART_IFC);
// and deposit it into RBR
// and set it full (LSR.DR = 1)
(* descending_urgency = "rl_receive, rl_process_rd_req" *)
rule rl_receive ((rg_lsr & uart_lsr_dr) == 0);
let ch <- pop (f_from_console);
rg_rbr <= ch;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
// Copyright (c) 2013-2020 Bluespec, Inc. All Rights Reserved.
//-
// RVFI_DII modifications:
@@ -33,9 +33,11 @@ package Top_HW_Side;
`include "ProcConfig.bsv"
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Clocks :: *;
// ----------------
// BSV additional libs
@@ -61,6 +63,7 @@ import C_Imports :: *;
`ifdef INCLUDE_GDB_CONTROL
import External_Control :: *;
import Debug_Module :: *;
`endif
`ifdef RVFI_DII
@@ -79,45 +82,124 @@ module mkTop_HW_Side (Empty);
`else
module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
`endif
SoC_Top_IFC soc_top <- mkSoC_Top;
Mem_Model_IFC mem_model <- mkMem_Model;
// ================================================================
// The RISC-V Debug Module is at the following point in the module hierarchy:
// soc_top.corew.debug_module
// (instances of mkSoC_Top, mkCoreW, mkDebug_Module)
// The Debug Module is reset only once, on power-up, hence we pass
// its reset down from here.
// (power-on reset) and the Debug Module's 'hart_reset' control.
let power_on_reset <- exposeCurrentReset;
let dm_power_on_reset = power_on_reset;
// The rest of the system (soc_top and mem_model) are reset:
// - on power-on, and
// - when the Debug Module requests an NDM reset (for non-DebugModule).
`ifdef INCLUDE_GDB_CONTROL
let clk <- exposeCurrentClock;
Bool initial_reset_val = False;
Integer ndm_reset_duration = 10; // NOTE: assuming 10 cycle reset enough for NDM
let ndm_reset_controller <- mkReset(ndm_reset_duration, initial_reset_val, clk);
let ndm_reset <- mkResetEither (power_on_reset, ndm_reset_controller.new_rst);
`else
let ndm_reset = power_on_reset;
`endif
// ================================================================
// STATE
SoC_Top_IFC soc_top <- mkSoC_Top (dm_power_on_reset, reset_by ndm_reset);
Mem_Model_IFC mem_model <- mkMem_Model (reset_by ndm_reset);
// Connect SoC to raw memory
let memCnx <- mkConnection (soc_top.to_raw_mem, mem_model.mem_server);
let memCnx <- mkConnection (soc_top.to_raw_mem, mem_model.mem_server, reset_by ndm_reset);
// ================================================================
// Actions on reset
function Action fa_reset_actions;
action
`ifndef RVFI_DII
$display ("================================================================");
$display ("Bluespec RISC-V standalone system simulation v1.2");
$display ("Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.");
$display ("================================================================");
`endif
// Set CPU verbosity and logdelay (simulation only)
Bool v1 <- $test$plusargs ("v1");
Bool v2 <- $test$plusargs ("v2");
Bit #(4) verbosity = ((v2 ? 2 : (v1 ? 1 : 0)));
Bit #(64) logdelay = 0; // # of instructions after which to set verbosity
soc_top.set_verbosity (verbosity, logdelay);
// ----------------
// Load optional tohost and fromhost addrs from symbol-table file
Fabric_Addr tohost_addr = 0;
Fabric_Addr fromhost_addr = 0;
Bool watch_tohost <- $test$plusargs ("tohost");
`ifndef IVERILOG
// Note: see 'CAVEAT FOR IVERILOG USERS' above
if (watch_tohost) begin
let tha <- c_get_symbol_val ("tohost");
tohost_addr = truncate (tha);
let fha <- c_get_symbol_val ("fromhost");
fromhost_addr = truncate (fha);
end
`endif
$display ("INFO: watch_tohost %d, tohost_addr = 0x%0h, fromhost_addr = 0x%0h",
watch_tohost, tohost_addr, fromhost_addr);
soc_top.start (tohost_addr, fromhost_addr);
endaction
endfunction
// ================================================================
`ifdef INCLUDE_GDB_CONTROL
// ================================================================
// NDM reset from DM
Reg #(Bit #(8)) rg_ndm_reset_delay <- mkReg (0);
rule rl_ndm_reset (rg_ndm_reset_delay == 0);
let x <- soc_top.ndm_reset_client.request.get;
ndm_reset_controller.assertReset;
rg_ndm_reset_delay <= fromInteger (ndm_reset_duration + 200); // NOTE: heuristic
$display ("%0d: %m.rl_ndm_reset: asserting NDM reset (for non-DebugModule) for %0d cycles",
cur_cycle, ndm_reset_duration);
endrule
rule rl_ndm_reset_wait (rg_ndm_reset_delay != 0);
if (rg_ndm_reset_delay == 1) begin
fa_reset_actions;
Bool is_running = True;
soc_top.ndm_reset_client.response.put (is_running);
$display ("%0d: %m.rl_ndm_reset_wait: sent NDM reset ack (for non-DebugModule) to Debug Module",
cur_cycle);
end
rg_ndm_reset_delay <= rg_ndm_reset_delay - 1;
endrule
// ================================================================
`endif
// ================================================================
// BEHAVIOR
`ifndef RVFI_DII
Reg #(Bool) rg_banner_printed <- mkReg (False);
// Display a banner
rule rl_step0 (! rg_banner_printed);
$display ("================================================================");
$display ("Bluespec RISC-V standalone system simulation v1.2");
$display ("Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.");
$display ("================================================================");
rg_banner_printed <= True;
// Set CPU verbosity and logdelay (simulation only)
Bool v1 <- $test$plusargs ("v1");
Bool v2 <- $test$plusargs ("v2");
Bit #(4) verbosity = ((v2 ? 2 : (v1 ? 1 : 0)));
Bit #(64) logdelay = 0; // # of instructions after which to set verbosity
soc_top.set_verbosity (verbosity, logdelay);
// ----------------
// Load tohost addr from symbol-table file
`ifndef IVERILOG
// Note: see 'CAVEAT FOR IVERILOG USERS' above
Bool watch_tohost <- $test$plusargs ("tohost");
let tha <- c_get_symbol_val ("tohost");
Fabric_Addr tohost_addr = truncate (tha);
$display ("INFO: watch_tohost = %0d, tohost_addr = 0x%0h",
pack (watch_tohost), tohost_addr);
soc_top.set_watch_tohost (watch_tohost, tohost_addr);
`endif
fa_reset_actions;
// ----------------
// Open file for Tandem Verification trace output
@@ -154,7 +236,6 @@ module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
`endif
endrule
`endif
// ================================================================
// Tandem verifier: drain and output vectors of bytes
@@ -229,6 +310,10 @@ module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
// Interaction with remote debug client
`ifdef INCLUDE_GDB_CONTROL
FIFOF #(Control_Req) f_external_control_reqs <- mkFIFOF;
FIFOF #(Control_Rsp) f_external_control_rsps <- mkFIFOF;
rule rl_debug_client_request_recv;
Bit #(64) req <- c_debug_client_request_recv ('hAA);
Bit #(8) status = req [63:56];
@@ -247,14 +332,14 @@ module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
let control_req = Control_Req {op: external_control_req_op_read_control_fabric,
arg1: zeroExtend (addr),
arg2: 0};
soc_top.server_external_control.request.put (control_req);
f_external_control_reqs.enq (control_req);
end
else if (op == dmi_op_write) begin
// $display (" WRITE 0x%0h 0x%0h", addr, data);
let control_req = Control_Req {op: external_control_req_op_write_control_fabric,
arg1: zeroExtend (addr),
arg2: zeroExtend (data)};
soc_top.server_external_control.request.put (control_req);
f_external_control_reqs.enq (control_req);
end
else if (op == dmi_op_shutdown) begin
$display ("Top_HW_Side.rl_debug_client_request_recv: SHUTDOWN");
@@ -269,7 +354,7 @@ module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
endrule
rule rl_debug_client_response_send;
let control_rsp <- soc_top.server_external_control.response.get;
let control_rsp <- pop (f_external_control_rsps);
// $display ("Top_HW_Side.rl_debug_client_response_send: 0x%0h", control_rsp.result);
let status <- c_debug_client_response_send (truncate (control_rsp.result));
if (status == dmi_status_err) begin
@@ -278,6 +363,59 @@ module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
$finish (1);
end
endrule
// ----------------------------------------------------------------
// External debug requests and responses
Control_Req req = f_external_control_reqs.first;
Integer dmi_verbosity = 0; // For debugging
rule rl_handle_external_req_read_request (req.op == external_control_req_op_read_control_fabric);
f_external_control_reqs.deq;
soc_top.dmi.read_addr (truncate (req.arg1));
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_read_request", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_read_response;
let x <- soc_top.dmi.read_data;
let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: signExtend (x)};
f_external_control_rsps.enq (rsp);
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_read_response", cur_cycle);
$display (" ", fshow (rsp));
end
endrule
rule rl_handle_external_req_write (req.op == external_control_req_op_write_control_fabric);
f_external_control_reqs.deq;
soc_top.dmi.write (truncate (req.arg1), truncate (req.arg2));
// let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: 0};
// f_external_control_rsps.enq (rsp);
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_write", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_err ( (req.op != external_control_req_op_read_control_fabric)
&& (req.op != external_control_req_op_write_control_fabric));
f_external_control_reqs.deq;
let rsp = Control_Rsp {status: external_control_rsp_status_err, result: 0};
f_external_control_rsps.enq (rsp);
$display ("%0d: %m.rl_handle_external_req_err: unknown req.op", cur_cycle);
$display (" ", fshow (req));
endrule
(* descending_urgency = "rl_handle_external_req_read_request, rl_handle_external_req_read_response" *)
(* descending_urgency = "rl_handle_external_req_read_response, rl_handle_external_req_write" *)
(* descending_urgency = "rl_handle_external_req_read_response, rl_handle_external_req_err" *)
(* descending_urgency = "rl_handle_external_req_write, rl_handle_external_req_err" *)
rule rl_handle_external_dummy_for_urgency_attribs_only;
endrule
`endif
// ================================================================