Initial load of files
This commit is contained in:
339
src_Testbench/Fabrics/AXI4/AXI4_Fabric.bsv
Normal file
339
src_Testbench/Fabrics/AXI4/AXI4_Fabric.bsv
Normal file
@@ -0,0 +1,339 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package AXI4_Fabric;
|
||||
|
||||
// ================================================================
|
||||
// This package defines a fabric connecting CPUs, Memories and DMAs
|
||||
// and other IP blocks.
|
||||
|
||||
// ================================================================
|
||||
// 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_Fabric_IFC #(numeric type num_masters,
|
||||
numeric type num_slaves,
|
||||
numeric type wd_id,
|
||||
numeric type wd_addr,
|
||||
numeric type wd_data,
|
||||
numeric type wd_user);
|
||||
method Action reset;
|
||||
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;
|
||||
|
||||
// To slaves
|
||||
interface Vector #(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
|
||||
// returns (True, slave-port-num) if address is mapped to slave-port-num
|
||||
// (False, ?) if address is unmapped to any port
|
||||
|
||||
module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(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))
|
||||
|
||||
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));
|
||||
|
||||
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))
|
||||
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))
|
||||
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)
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BEHAVIOR
|
||||
|
||||
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
|
||||
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_rd_sjs [mi].clear;
|
||||
v_f_rd_err_id [mi].clear;
|
||||
v_f_rd_err_user [mi].clear;
|
||||
end
|
||||
|
||||
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) begin
|
||||
xactors_to_slaves [sj].reset;
|
||||
v_f_wr_mis [sj].clear;
|
||||
v_f_rd_mis [sj].clear;
|
||||
end
|
||||
rg_reset <= False;
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Help functions for moving data from masters to slaves
|
||||
|
||||
Integer num_slaves_i = valueOf (num_slaves);
|
||||
|
||||
function Bool wr_move_from_mi_to_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)
|
||||
|| (slave_num == fromInteger (sj))));
|
||||
endfunction
|
||||
|
||||
function Bool wr_illegal_sj (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);
|
||||
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)
|
||||
|| (slave_num == fromInteger (sj))));
|
||||
endfunction
|
||||
|
||||
function Bool rd_illegal_sj (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
|
||||
|
||||
// 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)
|
||||
|
||||
rule rl_wr_xaction_master_to_slave (wr_move_from_mi_to_sj (mi, sj));
|
||||
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);
|
||||
|
||||
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));
|
||||
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));
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Fabric: wr master [%0d] -> illegal addr", cur_cycle, mi);
|
||||
$display (" ", fshow (a));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd requests from masters to slaves
|
||||
|
||||
// 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)
|
||||
|
||||
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);
|
||||
|
||||
xactors_to_slaves [sj].i_rd_addr.enq (a);
|
||||
|
||||
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));
|
||||
end
|
||||
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);
|
||||
|
||||
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)
|
||||
|
||||
rule rl_wr_resp_slave_to_master ( (v_f_wr_mis [sj].first == fromInteger (mi))
|
||||
&& (v_f_wr_sjs [mi].first == fromInteger (sj)));
|
||||
v_f_wr_mis [sj].deq;
|
||||
v_f_wr_sjs [mi].deq;
|
||||
AXI4_Wr_Resp #(wd_id, wd_user) b <- pop_o (xactors_to_slaves [sj].o_wr_resp);
|
||||
|
||||
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);
|
||||
$display (" ", fshow (b));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Wr error responses to masters
|
||||
// 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
|
||||
|
||||
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
|
||||
|
||||
rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (valueOf (num_slaves)));
|
||||
v_f_wr_sjs [mi].deq;
|
||||
v_f_wr_err_id [mi].deq;
|
||||
v_f_wr_err_user [mi].deq;
|
||||
|
||||
let b = AXI4_Wr_Resp {bid: v_f_wr_err_id [mi].first,
|
||||
bresp: axi4_resp_decerr,
|
||||
buser: v_f_wr_err_user [mi].first};
|
||||
|
||||
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);
|
||||
$display (" ", fshow (b));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd 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)
|
||||
|
||||
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);
|
||||
|
||||
xactors_from_masters [mi].i_rd_data.enq (r);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Fabric: rd master [%0d] <- slave [%0d]", cur_cycle, mi, sj);
|
||||
$display (" ", fshow (r));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd error responses to masters
|
||||
// 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
|
||||
|
||||
for (Integer mi = 0; mi < valueOf (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;
|
||||
|
||||
Bit #(wd_data) data = 0;
|
||||
let r = AXI4_Rd_Data {rid: v_f_rd_err_id [mi].first,
|
||||
rdata: data,
|
||||
rresp: axi4_resp_decerr,
|
||||
rlast: True,
|
||||
ruser: v_f_rd_err_user [mi].first};
|
||||
|
||||
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));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
function AXI4_Slave_IFC #(wd_id, wd_addr, wd_data, wd_user) f1 (Integer j)
|
||||
= xactors_from_masters [j].axi_side;
|
||||
function AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user) f2 (Integer j)
|
||||
= xactors_to_slaves [j].axi_side;
|
||||
|
||||
method Action reset () if (! rg_reset);
|
||||
rg_reset <= True;
|
||||
endmethod
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
cfg_verbosity <= verbosity;
|
||||
endmethod
|
||||
|
||||
interface v_from_masters = genWith (f1);
|
||||
interface v_to_slaves = genWith (f2);
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage: AXI4_Fabric
|
||||
1384
src_Testbench/Fabrics/AXI4/AXI4_Types.bsv
Normal file
1384
src_Testbench/Fabrics/AXI4/AXI4_Types.bsv
Normal file
File diff suppressed because it is too large
Load Diff
322
src_Testbench/Fabrics/AXI4_Lite/AXI4_Lite_Fabric.bsv
Normal file
322
src_Testbench/Fabrics/AXI4_Lite/AXI4_Lite_Fabric.bsv
Normal file
@@ -0,0 +1,322 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package AXI4_Lite_Fabric;
|
||||
|
||||
// ================================================================
|
||||
// This package defines a fabric connecting CPUs, Memories and DMAs
|
||||
// and other IP blocks.
|
||||
|
||||
// ================================================================
|
||||
// Bluespec library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import SpecialFIFOs :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
import AXI4_Lite_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// The interface for the fabric module
|
||||
|
||||
interface AXI4_Lite_Fabric_IFC #(numeric type num_masters,
|
||||
numeric type num_slaves,
|
||||
numeric type wd_addr,
|
||||
numeric type wd_data,
|
||||
numeric type wd_user);
|
||||
method Action reset;
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
|
||||
// From masters
|
||||
interface Vector #(num_masters, AXI4_Lite_Slave_IFC #(wd_addr, wd_data, wd_user)) v_from_masters;
|
||||
|
||||
// To slaves
|
||||
interface Vector #(num_slaves, AXI4_Lite_Master_IFC #(wd_addr, wd_data, wd_user)) v_to_slaves;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// The Fabric module
|
||||
// The function parameter is an address-decode function, which returns
|
||||
// returns (True, slave-port-num) if address is mapped to slave-port-num
|
||||
// (False, ?) if address is unmapped to any port
|
||||
|
||||
module mkAXI4_Lite_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(num_slaves)))
|
||||
fn_addr_to_slave_num (Bit #(wd_addr) addr))
|
||||
(AXI4_Lite_Fabric_IFC #(num_masters, num_slaves, 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));
|
||||
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
Reg #(Bool) rg_reset <- mkReg (True);
|
||||
|
||||
// Transactors facing masters
|
||||
Vector #(num_masters, AXI4_Lite_Slave_Xactor_IFC #(wd_addr, wd_data, wd_user))
|
||||
xactors_from_masters <- replicateM (mkAXI4_Lite_Slave_Xactor);
|
||||
|
||||
// Transactors facing slaves
|
||||
Vector #(num_slaves, AXI4_Lite_Master_Xactor_IFC #(wd_addr, wd_data, wd_user))
|
||||
xactors_to_slaves <- replicateM (mkAXI4_Lite_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)
|
||||
|
||||
Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- 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));
|
||||
|
||||
Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- 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));
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BEHAVIOR
|
||||
|
||||
rule rl_reset (rg_reset);
|
||||
$display ("%0d: AXI4_Lite_Fabric.rl_reset", cur_cycle);
|
||||
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) begin
|
||||
xactors_from_masters [mi].reset;
|
||||
|
||||
v_f_wr_sjs [mi].clear;
|
||||
v_f_wr_err_user [mi].clear;
|
||||
|
||||
v_f_rd_sjs [mi].clear;
|
||||
v_f_rd_err_user [mi].clear;
|
||||
end
|
||||
|
||||
for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) begin
|
||||
xactors_to_slaves [sj].reset;
|
||||
v_f_wr_mis [sj].clear;
|
||||
v_f_rd_mis [sj].clear;
|
||||
end
|
||||
rg_reset <= False;
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Help functions for moving data from masters to slaves
|
||||
|
||||
Integer num_slaves_i = valueOf (num_slaves);
|
||||
|
||||
function Bool wr_move_from_mi_to_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)
|
||||
|| (slave_num == fromInteger (sj))));
|
||||
endfunction
|
||||
|
||||
function Bool wr_illegal_sj (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);
|
||||
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)
|
||||
|| (slave_num == fromInteger (sj))));
|
||||
endfunction
|
||||
|
||||
function Bool rd_illegal_sj (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
|
||||
|
||||
// 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)
|
||||
|
||||
rule rl_wr_xaction_master_to_slave (wr_move_from_mi_to_sj (mi, sj));
|
||||
AXI4_Lite_Wr_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
|
||||
AXI4_Lite_Wr_Data #(wd_data) 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);
|
||||
|
||||
v_f_wr_mis [sj].enq (fromInteger (mi));
|
||||
v_f_wr_sjs [mi].enq (fromInteger (sj));
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: wr master [%0d] -> slave [%0d]", cur_cycle, mi, sj);
|
||||
$display (" ", fshow (a));
|
||||
$display (" ", fshow (d));
|
||||
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));
|
||||
AXI4_Lite_Wr_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr);
|
||||
AXI4_Lite_Wr_Data #(wd_data) d <- pop_o (xactors_from_masters [mi].o_wr_data);
|
||||
|
||||
v_f_wr_sjs [mi].enq (fromInteger (valueOf (num_slaves)));
|
||||
v_f_wr_err_user [mi].enq (a.awuser);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: wr master [%0d] -> illegal addr", cur_cycle, mi);
|
||||
$display (" ", fshow (a));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd requests from masters to slaves
|
||||
|
||||
// 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)
|
||||
|
||||
rule rl_rd_xaction_master_to_slave (rd_move_from_mi_to_sj (mi, sj));
|
||||
AXI4_Lite_Rd_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
|
||||
|
||||
xactors_to_slaves [sj].i_rd_addr.enq (a);
|
||||
|
||||
v_f_rd_mis [sj].enq (fromInteger (mi));
|
||||
v_f_rd_sjs [mi].enq (fromInteger (sj));
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: rd master [%0d] -> slave [%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_rd_xaction_no_such_slave (rd_illegal_sj (mi));
|
||||
AXI4_Lite_Rd_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr);
|
||||
|
||||
v_f_rd_sjs [mi].enq (fromInteger (valueOf (num_slaves)));
|
||||
v_f_rd_err_user [mi].enq (a.aruser);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_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)
|
||||
|
||||
rule rl_wr_resp_slave_to_master ( (v_f_wr_mis [sj].first == fromInteger (mi))
|
||||
&& (v_f_wr_sjs [mi].first == fromInteger (sj)));
|
||||
v_f_wr_mis [sj].deq;
|
||||
v_f_wr_sjs [mi].deq;
|
||||
AXI4_Lite_Wr_Resp #(wd_user) b <- pop_o (xactors_to_slaves [sj].o_wr_resp);
|
||||
|
||||
xactors_from_masters [mi].i_wr_resp.enq (b);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: wr master [%0d] <- slave [%0d]", cur_cycle, mi, sj);
|
||||
$display (" ", fshow (b));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Wr error responses to masters
|
||||
// v_f_wr_sjs [mi].first has value num_slaves (illegal value)
|
||||
// v_f_wr_err_user [mi].first contains the request's 'user' data
|
||||
|
||||
for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1)
|
||||
|
||||
rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (valueOf (num_slaves)));
|
||||
v_f_wr_sjs [mi].deq;
|
||||
v_f_wr_err_user [mi].deq;
|
||||
|
||||
let b = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_DECERR, buser: v_f_wr_err_user [mi].first};
|
||||
|
||||
xactors_from_masters [mi].i_wr_resp.enq (b);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: wr master [%0d] <- error", cur_cycle, mi);
|
||||
$display (" ", fshow (b));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd 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)
|
||||
|
||||
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_Lite_Rd_Data #(wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data);
|
||||
|
||||
xactors_from_masters [mi].i_rd_data.enq (r);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: rd master [%0d] <- slave [%0d]", cur_cycle, mi, sj);
|
||||
$display (" ", fshow (r));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Rd error responses to masters
|
||||
// v_f_rd_sjs [mi].first has value num_slaves (illegal value)
|
||||
// v_f_rd_err_user [mi].first contains the request's 'user' data
|
||||
|
||||
for (Integer mi = 0; mi < valueOf (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_user [mi].deq;
|
||||
|
||||
Bit #(wd_data) data = 0;
|
||||
let r = AXI4_Lite_Rd_Data {rresp: AXI4_LITE_DECERR, ruser: v_f_rd_err_user [mi].first, rdata: data};
|
||||
|
||||
xactors_from_masters [mi].i_rd_data.enq (r);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: AXI4_Lite_Fabric: rd master [%0d] <- error", cur_cycle, mi);
|
||||
$display (" ", fshow (r));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
function AXI4_Lite_Slave_IFC #(wd_addr, wd_data, wd_user) f1 (Integer j)
|
||||
= xactors_from_masters [j].axi_side;
|
||||
function AXI4_Lite_Master_IFC #(wd_addr, wd_data, wd_user) f2 (Integer j)
|
||||
= xactors_to_slaves [j].axi_side;
|
||||
|
||||
method Action reset () if (! rg_reset);
|
||||
rg_reset <= True;
|
||||
endmethod
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
cfg_verbosity <= verbosity;
|
||||
endmethod
|
||||
|
||||
interface v_from_masters = genWith (f1);
|
||||
interface v_to_slaves = genWith (f2);
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage: AXI4_Lite_Fabric
|
||||
1020
src_Testbench/Fabrics/AXI4_Lite/AXI4_Lite_Types.bsv
Normal file
1020
src_Testbench/Fabrics/AXI4_Lite/AXI4_Lite_Types.bsv
Normal file
File diff suppressed because it is too large
Load Diff
151
src_Testbench/Fabrics/Adapters/AXI4_AXI4_Lite_Adapters.bsv
Normal file
151
src_Testbench/Fabrics/Adapters/AXI4_AXI4_Lite_Adapters.bsv
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package AXI4_AXI4_Lite_Adapters;
|
||||
|
||||
// ================================================================
|
||||
// Adapters for interconnecting AXI4 and AXI4_Lite.
|
||||
|
||||
// Ref: ARM document:
|
||||
// AMBA AXI and ACE Protocol Specification
|
||||
// AXI3, AXI4, and AXI4-Lite
|
||||
// ACE and ACE-Lite
|
||||
// ARM IHI 0022E (ID022613)
|
||||
// Issue E, 22 Feb 2013
|
||||
|
||||
// See export list below
|
||||
|
||||
// ================================================================
|
||||
// Exports
|
||||
|
||||
export
|
||||
|
||||
fn_AXI4_Lite_Master_IFC_to_AXI4_Master_IFC;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
import EdgeFIFOFs :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Lite_Types :: *;
|
||||
import AXI4_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// Compute the encoding of AWSIZE/ARSIZE
|
||||
|
||||
function Bit #(3) wd_data_to_axsize (Integer wd_data_i);
|
||||
Bit #(3) axsize = ( (wd_data_i == 32)
|
||||
? 3'b_010
|
||||
: ( (wd_data_i == 64)
|
||||
? 3'b_011
|
||||
: 3'b_000));
|
||||
return axsize;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
function AXI4_Master_IFC #(wd_id, wd_addr, wd_data, wd_user)
|
||||
fn_AXI4_Lite_Master_IFC_to_AXI4_Master_IFC
|
||||
(AXI4_Lite_Master_IFC #(wd_addr, wd_data, wd_user) axi4_lite);
|
||||
|
||||
return
|
||||
interface AXI4_Master_IFC;
|
||||
|
||||
// ----------------
|
||||
// Wr Addr channel
|
||||
// output buses
|
||||
method Bool m_awvalid = axi4_lite.m_awvalid;
|
||||
|
||||
method Bit #(wd_id) m_awid = 0;
|
||||
method Bit #(wd_addr) m_awaddr = axi4_lite.m_awaddr;
|
||||
method Bit #(8) m_awlen = 0; // burst length = awlen+1
|
||||
method Bit #(3) m_awsize = wd_data_to_axsize (valueOf (wd_data));
|
||||
method Bit #(2) m_awburst = 2'b_00; // FIXED
|
||||
method Bit #(1) m_awlock = 0; // NORMAL
|
||||
method Bit #(4) m_awcache = 4'b_0000; // Device Non-Bufferable
|
||||
method Bit #(3) m_awprot = axi4_lite.m_awprot;
|
||||
method Bit #(4) m_awqos = 4'b_0000;
|
||||
method Bit #(4) m_awregion = 4'b_0000;
|
||||
method Bit #(wd_user) m_awuser = 0;
|
||||
|
||||
// input buses
|
||||
method Action m_awready (Bool awready) = axi4_lite.m_awready (awready);
|
||||
|
||||
// ----------------
|
||||
// Wr Data channel
|
||||
// output buses
|
||||
method Bool m_wvalid = axi4_lite.m_wvalid;
|
||||
|
||||
method Bit #(wd_id) m_wid = 0;
|
||||
method Bit #(wd_data) m_wdata = axi4_lite.m_wdata;
|
||||
method Bit #(TDiv #(wd_data, 8)) m_wstrb = axi4_lite.m_wstrb;
|
||||
method Bool m_wlast = True;
|
||||
method Bit #(wd_user) m_wuser = 0;
|
||||
|
||||
// input buses
|
||||
method Action m_wready (Bool wready) = axi4_lite.m_wready (wready);
|
||||
|
||||
// ----------------
|
||||
// Wr Response channel
|
||||
// input buses
|
||||
method Action m_bvalid (Bool bvalid,
|
||||
Bit #(wd_id) bid,
|
||||
Bit #(2) bresp,
|
||||
Bit #(wd_user) buser) = axi4_lite.m_bvalid (bvalid,
|
||||
bresp,
|
||||
0);
|
||||
|
||||
// output buses
|
||||
method Bool m_bready = axi4_lite.m_bready;
|
||||
|
||||
// ----------------
|
||||
// Rd Addr channel
|
||||
// output buses
|
||||
method Bool m_arvalid = axi4_lite.m_arvalid;
|
||||
|
||||
method Bit #(wd_id) m_arid = 0;
|
||||
method Bit #(wd_addr) m_araddr = axi4_lite.m_araddr;
|
||||
method Bit #(8) m_arlen = 0; // burst length = awlen+1
|
||||
method Bit #(3) m_arsize = wd_data_to_axsize (valueOf (wd_data));
|
||||
method Bit #(2) m_arburst = 2'b_00; // FIXED
|
||||
method Bit #(1) m_arlock = 0; // NORMAL
|
||||
method Bit #(4) m_arcache = 4'b_0000; // Device Non-Bufferable
|
||||
method Bit #(3) m_arprot = axi4_lite.m_arprot;
|
||||
method Bit #(4) m_arqos = 4'b_0000;
|
||||
method Bit #(4) m_arregion = 4'b_0000;
|
||||
method Bit #(wd_user) m_aruser = axi4_lite.m_aruser;
|
||||
|
||||
// input buses
|
||||
method Action m_arready (Bool arready) = axi4_lite.m_arready (arready);
|
||||
|
||||
// ----------------
|
||||
// Rd Data channel
|
||||
// input buses
|
||||
method Action m_rvalid (Bool rvalid,
|
||||
Bit #(wd_id) rid,
|
||||
Bit #(wd_data) rdata,
|
||||
Bit #(2) rresp,
|
||||
Bool rlast,
|
||||
Bit #(wd_user) ruser) = axi4_lite.m_rvalid (rvalid,
|
||||
rresp,
|
||||
rdata,
|
||||
0);
|
||||
|
||||
// output buses
|
||||
method Bool m_rready = axi4_lite.m_rready;
|
||||
|
||||
endinterface;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
201
src_Testbench/SoC/Boot_ROM.bsv
Normal file
201
src_Testbench/SoC/Boot_ROM.bsv
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Boot_ROM;
|
||||
|
||||
// ================================================================
|
||||
// This package implements a slave IP that is a RISC-V boot ROM of
|
||||
// 1024 32b locations.
|
||||
// - Ignores all writes, always responsing OKAY
|
||||
// - Assumes all reads are 4-byte aligned requests for 4-bytes
|
||||
|
||||
// ================================================================
|
||||
|
||||
export Boot_ROM_IFC (..), mkBoot_ROM;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// Include the auto-generated BSV-include file with the ROM function
|
||||
|
||||
`ifdef RV32
|
||||
`include "fn_read_ROM_RV32.bsvi"
|
||||
`endif
|
||||
|
||||
`ifdef RV64
|
||||
`include "fn_read_ROM_RV64.bsvi"
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface Boot_ROM_IFC;
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkBoot_ROM (Boot_ROM_IFC);
|
||||
|
||||
// Verbosity: 0: quiet; 1: reads/writes
|
||||
Integer verbosity = 0;
|
||||
|
||||
Reg #(Bool) rg_module_ready <- mkReg (False);
|
||||
|
||||
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
|
||||
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
|
||||
|
||||
// ----------------
|
||||
// Connector to fabric
|
||||
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// ----------------
|
||||
|
||||
function Bool fn_addr_is_aligned (Fabric_Addr addr);
|
||||
if (valueOf (Wd_Data) == 32)
|
||||
return (addr [1:0] == 2'b_00);
|
||||
else if (valueOf (Wd_Data) == 64)
|
||||
return (addr [2:0] == 3'b_000);
|
||||
else
|
||||
return False;
|
||||
endfunction
|
||||
|
||||
function Bool fn_addr_is_in_range (Fabric_Addr base, Fabric_Addr addr, Fabric_Addr lim);
|
||||
return ((base <= addr) && (addr < lim));
|
||||
endfunction
|
||||
|
||||
function Bool fn_addr_is_ok (Fabric_Addr base, Fabric_Addr addr, Fabric_Addr lim);
|
||||
return ( fn_addr_is_aligned (addr)
|
||||
&& fn_addr_is_in_range (base, addr, lim));
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric read requests
|
||||
|
||||
rule rl_process_rd_req (rg_module_ready);
|
||||
let rda <- pop_o (slave_xactor.o_rd_addr);
|
||||
|
||||
let byte_addr = rda.araddr - rg_addr_base;
|
||||
|
||||
AXI4_Resp rresp = axi4_resp_okay;
|
||||
Bit #(64) data64 = 0;
|
||||
if (! fn_addr_is_ok (rg_addr_base, rda.araddr, rg_addr_lim)) begin
|
||||
rresp = axi4_resp_slverr;
|
||||
$display ("%0d: ERROR: Boot_ROM.rl_process_rd_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
else if (rda.araddr [2:0] == 3'b0) begin
|
||||
Bit #(32) d0 = fn_read_ROM_0 (byte_addr);
|
||||
Bit #(32) d1 = fn_read_ROM_4 (byte_addr + 4);
|
||||
data64 = { d1, d0 };
|
||||
end
|
||||
else begin // ((valueOf (Wd_Data) == 32) && (rda.addr [1:0] == 2'b_00))
|
||||
Bit #(32) d1 = fn_read_ROM_4 (byte_addr);
|
||||
data64 = { 0, d1 };
|
||||
end
|
||||
|
||||
Bit #(Wd_Data) rdata = truncate (data64);
|
||||
let rdr = AXI4_Rd_Data {rid: rda.arid,
|
||||
rdata: rdata,
|
||||
rresp: rresp,
|
||||
rlast: True,
|
||||
ruser: rda.aruser};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("%0d: Boot_ROM.rl_process_rd_req: ", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
$display (" => ", fshow (rdr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric write requests: ignore all of them (this is a ROM)
|
||||
|
||||
rule rl_process_wr_req (rg_module_ready);
|
||||
let wra <- pop_o (slave_xactor.o_wr_addr);
|
||||
let wrd <- pop_o (slave_xactor.o_wr_data);
|
||||
|
||||
AXI4_Resp bresp = axi4_resp_okay;
|
||||
if (! fn_addr_is_ok (rg_addr_base, wra.awaddr, rg_addr_lim)) begin
|
||||
bresp = axi4_resp_slverr;
|
||||
$display ("%0d: ERROR: Boot_ROM.rl_process_wr_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
end
|
||||
|
||||
let wrr = AXI4_Wr_Resp {bid: wra.awid,
|
||||
bresp: bresp,
|
||||
buser: wra.awuser};
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
|
||||
if (verbosity > 0) begin
|
||||
$display ("%0d: Boot_ROM.rl_process_wr_req; ignoring all writes", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
$display (" => ", fshow (wrr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
if (valueOf (Wd_Data) == 32) begin
|
||||
if (addr_base [1:0] != 0)
|
||||
$display ("%0d: WARNING: Boot_ROM.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_base);
|
||||
|
||||
if (addr_lim [1:0] != 0)
|
||||
$display ("%0d: WARNING: Boot_ROM.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_lim);
|
||||
end
|
||||
else if (valueOf (Wd_Data) == 64) begin
|
||||
if (addr_base [2:0] != 0)
|
||||
$display ("%0d: WARNING: Boot_ROM.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_base);
|
||||
|
||||
if (addr_lim [2:0] != 0)
|
||||
$display ("%0d: WARNING: Boot_ROM.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_lim);
|
||||
end
|
||||
|
||||
rg_addr_base <= addr_base;
|
||||
rg_addr_lim <= addr_lim;
|
||||
rg_module_ready <= True;
|
||||
if (verbosity > 0) begin
|
||||
$display ("%0d: Boot_ROM.set_addr_map: base 0x%0h lim 0x%0h", cur_cycle, addr_base, addr_lim);
|
||||
end
|
||||
endmethod
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface slave = slave_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
1
src_Testbench/SoC/Boot_ROM_Generator/.gitignore
vendored
Normal file
1
src_Testbench/SoC/Boot_ROM_Generator/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.exe
|
||||
113
src_Testbench/SoC/Boot_ROM_Generator/Gen_BSV_fn_read_ROM.py
Executable file
113
src_Testbench/SoC/Boot_ROM_Generator/Gen_BSV_fn_read_ROM.py
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
# ================================================================
|
||||
# Reads a memhex file created by gen_bootroom.cc
|
||||
# and constructs a BSV function representating that rom.
|
||||
|
||||
# ================================================================
|
||||
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
|
||||
# ================================================================
|
||||
|
||||
def main (argv = None):
|
||||
if ((len (argv) > 1) and ((argv [1] == "-h") or (argv [1] == "--help"))):
|
||||
print_usage (argv)
|
||||
return 0
|
||||
|
||||
if (len (argv) != 3):
|
||||
print_usage (argv)
|
||||
return 1
|
||||
|
||||
try:
|
||||
fin = open (argv [1], 'r')
|
||||
except:
|
||||
sys.stderr.write ("ERROR: unable to open or read file '{0}' for input\n".format (argv [1]))
|
||||
return 1
|
||||
|
||||
try:
|
||||
fout = open (argv [2], 'w')
|
||||
except:
|
||||
sys.stderr.write ("ERROR: unable to open file '{0}' for output\n".format (argv [2]))
|
||||
return 1
|
||||
|
||||
addr_lim = gen_BSV_ROM_function (fin, fout)
|
||||
sys.stderr.write ("Wrote BSV ROM function into file '{0}'; address limit is: {1}\n".format (argv [2], addr_lim))
|
||||
|
||||
fin.close ()
|
||||
fout.close ()
|
||||
return 0
|
||||
|
||||
# ================================================================
|
||||
|
||||
def print_usage (argv):
|
||||
sys.stdout.write ("Usage: {0} <memhex input filename> <BSV output filename>\n".format (argv [0]))
|
||||
sys.stdout.write (" The memhex file should contain 32b words\n")
|
||||
|
||||
# ================================================================
|
||||
|
||||
def gen_BSV_ROM_function (fin, fout):
|
||||
|
||||
# Read the memhex file
|
||||
lines = fin.readlines ()
|
||||
|
||||
# Generate the arms of the BSV case-statement.
|
||||
case_arms_0 = []
|
||||
case_arms_4 = []
|
||||
line_num = 1
|
||||
addr = 0
|
||||
for line in lines:
|
||||
line = line.strip ()
|
||||
if line.startswith ('@'):
|
||||
sys.stdout.write ("Note: ignoring line {0}: '{1}'\n".format (line_num, line))
|
||||
else:
|
||||
if ((addr % 8) == 0):
|
||||
case_arms_0.append (" {0}: 32'h_{1};\n".format (addr, line))
|
||||
else:
|
||||
case_arms_4.append (" {0}: 32'h_{1};\n".format (addr, line))
|
||||
addr = addr + 4
|
||||
line_num = line_num + 1
|
||||
|
||||
iso_utc_time = datetime.datetime.utcnow ().isoformat ()
|
||||
|
||||
fout.write ("// ***** DO NOT EDIT *****\n")
|
||||
fout.write ("// ***** This file was generated from a script *****\n")
|
||||
fout.write ("// Generated at UTC {0}\n".format (iso_utc_time))
|
||||
fout.write ("\n")
|
||||
fout.write ("\n")
|
||||
fout.write ("// This file is a BSV 'include' file\n")
|
||||
fout.write ("// The function below represents a ROM of {0} bytes\n".format (addr))
|
||||
fout.write ("\n")
|
||||
fout.write ("\n")
|
||||
fout.write ("// Function for 4-bytes values at addrs aligned to 'b000\n")
|
||||
fout.write ("\n")
|
||||
fout.write ("function Bit #(32) fn_read_ROM_0 (Bit #(n) addr);\n")
|
||||
fout.write (" return\n")
|
||||
fout.write (" case (addr)\n")
|
||||
for case_arm in case_arms_0:
|
||||
fout.write (case_arm)
|
||||
fout.write (" default: 32'h_AAAA_AAAA;\n")
|
||||
fout.write (" endcase;\n")
|
||||
fout.write ("endfunction: fn_read_ROM_0\n")
|
||||
fout.write ("\n")
|
||||
fout.write ("// Function for 4-bytes values at addrs aligned to 'b100\n")
|
||||
fout.write ("\n")
|
||||
fout.write ("function Bit #(32) fn_read_ROM_4 (Bit #(n) addr);\n")
|
||||
fout.write (" return\n")
|
||||
fout.write (" case (addr)\n")
|
||||
for case_arm in case_arms_4:
|
||||
fout.write (case_arm)
|
||||
fout.write (" default: 32'h_AAAA_AAAA;\n")
|
||||
fout.write (" endcase;\n")
|
||||
fout.write ("endfunction: fn_read_ROM_4\n")
|
||||
|
||||
return addr
|
||||
|
||||
# ================================================================
|
||||
# For non-interactive invocations, call main() and use its return value
|
||||
# as the exit code.
|
||||
if __name__ == '__main__':
|
||||
sys.exit (main (sys.argv))
|
||||
23
src_Testbench/SoC/Boot_ROM_Generator/Makefile
Normal file
23
src_Testbench/SoC/Boot_ROM_Generator/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generates fn_read_ROM_RV32.bsvi and fn_read_ROM_RV64.bsvi
|
||||
# in a temporary directory 'tmpdir/'.
|
||||
# Manually copy these to .., if ok.
|
||||
|
||||
default: tmpdir/fn_read_ROM_RV32.bsvi tmpdir/fn_read_ROM_RV64.bsvi
|
||||
|
||||
tmpdir/fn_read_ROM_RV64.bsvi: tmpdir gen_bootrom.exe
|
||||
./gen_bootrom.exe RV64 imaus > tmpdir/boot_ROM_RV64.memhex
|
||||
./Gen_BSV_fn_read_ROM.py tmpdir/boot_ROM_RV64.memhex tmpdir/fn_read_ROM_RV64.bsvi
|
||||
|
||||
tmpdir/fn_read_ROM_RV32.bsvi: tmpdir gen_bootrom.exe
|
||||
./gen_bootrom.exe RV32 imaus > tmpdir/boot_ROM_RV32.memhex
|
||||
./Gen_BSV_fn_read_ROM.py tmpdir/boot_ROM_RV32.memhex tmpdir/fn_read_ROM_RV32.bsvi
|
||||
|
||||
gen_bootrom.exe: tmpdir gen_bootrom.cc
|
||||
$(CXX) -o $@ -std=c++11 gen_bootrom.cc
|
||||
|
||||
tmpdir:
|
||||
mkdir -p tmpdir
|
||||
|
||||
.PHONY: full_clean
|
||||
full_clean:
|
||||
rm -r -f *~ tmpdir gen_bootrom.exe
|
||||
302
src_Testbench/SoC/Boot_ROM_Generator/gen_bootrom.cc
Normal file
302
src_Testbench/SoC/Boot_ROM_Generator/gen_bootrom.cc
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright (c) 2010-2019, The Regents of the University of California
|
||||
// (Regents). All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. Neither the name of the Regents nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
|
||||
// OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
|
||||
// BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
|
||||
// HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
|
||||
// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
// Modifications by Bluespec, Inc, 2018
|
||||
|
||||
// ================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <cinttypes>
|
||||
//#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
// ================================================================
|
||||
|
||||
#define DTC "/usr/bin/dtc"
|
||||
|
||||
// ================================================================
|
||||
|
||||
#define ROM_BASE 0x00001000
|
||||
|
||||
#define DMEM_BASE 0x80000000
|
||||
#define DMEM_SIZE 0x10000000
|
||||
|
||||
#define CLINT_BASE 0x02000000
|
||||
#define CLINT_SIZE 0x000c0000
|
||||
|
||||
#define UART_BASE 0xc0000000
|
||||
|
||||
#define NUM_PROCS 1
|
||||
|
||||
// -------------------------
|
||||
// Timer configuration
|
||||
|
||||
// For Piccolo Sim
|
||||
//
|
||||
#define CPU_HZ 50000lu
|
||||
|
||||
// #define CPU_HZ 10000000lu
|
||||
|
||||
// ================================================================
|
||||
|
||||
static std::string dts;
|
||||
|
||||
static std::string dts_compile(const std::string& dts)
|
||||
{
|
||||
// Convert the DTS to DTB
|
||||
int dts_pipe[2];
|
||||
pid_t dts_pid;
|
||||
|
||||
if (pipe(dts_pipe) != 0 || (dts_pid = fork()) < 0) {
|
||||
std::cerr << "Failed to fork dts child: " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Child process to output dts
|
||||
if (dts_pid == 0) {
|
||||
close(dts_pipe[0]);
|
||||
int step, len = dts.length();
|
||||
const char *buf = dts.c_str();
|
||||
for (int done = 0; done < len; done += step) {
|
||||
step = write(dts_pipe[1], buf+done, len-done);
|
||||
if (step == -1) {
|
||||
std::cerr << "Failed to write dts: " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
close(dts_pipe[1]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pid_t dtb_pid;
|
||||
int dtb_pipe[2];
|
||||
if (pipe(dtb_pipe) != 0 || (dtb_pid = fork()) < 0) {
|
||||
std::cerr << "Failed to fork dtb child: " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Child process to output dtb
|
||||
if (dtb_pid == 0) {
|
||||
dup2(dts_pipe[0], 0);
|
||||
dup2(dtb_pipe[1], 1);
|
||||
close(dts_pipe[0]);
|
||||
close(dts_pipe[1]);
|
||||
close(dtb_pipe[0]);
|
||||
close(dtb_pipe[1]);
|
||||
execl(DTC, DTC, "-O", "dtb", 0);
|
||||
std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(dts_pipe[1]);
|
||||
close(dts_pipe[0]);
|
||||
close(dtb_pipe[1]);
|
||||
|
||||
// Read-out dtb
|
||||
std::stringstream dtb;
|
||||
|
||||
int got;
|
||||
char buf[4096];
|
||||
while ((got = read(dtb_pipe[0], buf, sizeof(buf))) > 0) {
|
||||
dtb.write(buf, got);
|
||||
}
|
||||
if (got == -1) {
|
||||
std::cerr << "Failed to read dtb: " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
close(dtb_pipe[0]);
|
||||
|
||||
// Reap children
|
||||
int status;
|
||||
waitpid(dts_pid, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
std::cerr << "Child dts process failed" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
waitpid(dtb_pid, &status, 0);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
std::cerr << "Child dtb process failed" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return dtb.str();
|
||||
}
|
||||
|
||||
void make_dtb(int xlen, char *isa_string)
|
||||
{
|
||||
const int reset_vec_size = 8;
|
||||
|
||||
uint64_t start_pc = DMEM_BASE;
|
||||
|
||||
uint32_t reset_vec[reset_vec_size] = {
|
||||
0x297, // auipc t0,0x0
|
||||
0x28593 + (reset_vec_size * 4 << 20), // addi a1, t0, &dtb
|
||||
0xf1402573, // csrr a0, mhartid
|
||||
xlen == 32 ?
|
||||
0x0182a283u : // lw t0,24(t0)
|
||||
0x0182b283u, // ld t0,24(t0)
|
||||
0x28067, // jr t0
|
||||
0,
|
||||
(uint32_t) (start_pc & 0xffffffff),
|
||||
(uint32_t) (start_pc >> 32)
|
||||
};
|
||||
|
||||
std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec));
|
||||
|
||||
std::stringstream s;
|
||||
s << std::dec <<
|
||||
"/dts-v1/;\n"
|
||||
"\n"
|
||||
"/ {\n"
|
||||
" #address-cells = <2>;\n"
|
||||
" #size-cells = <2>;\n"
|
||||
" compatible = \"ucbbar,spike-bare-dev\";\n"
|
||||
" model = \"ucbbar,spike-bare\";\n"
|
||||
" cpus {\n"
|
||||
" #address-cells = <1>;\n"
|
||||
" #size-cells = <0>;\n"
|
||||
" timebase-frequency = <" << CPU_HZ << ">;\n";
|
||||
// For each processor
|
||||
for (size_t i = 0; i < NUM_PROCS; i++) {
|
||||
s << " CPU" << i << ": cpu@" << i << " {\n"
|
||||
" device_type = \"cpu\";\n"
|
||||
" reg = <" << i << ">;\n"
|
||||
" status = \"okay\";\n"
|
||||
" compatible = \"riscv\";\n"
|
||||
" riscv,isa = \"rv" << xlen << isa_string << "\";\n"
|
||||
" mmu-type = \"riscv," << (xlen <= 32 ? "sv32" : "sv48") << "\";\n"
|
||||
" clock-frequency = <" << CPU_HZ << ">;\n"
|
||||
" CPU" << i << "_intc: interrupt-controller {\n"
|
||||
" #interrupt-cells = <1>;\n"
|
||||
" interrupt-controller;\n"
|
||||
" compatible = \"riscv,cpu-intc\";\n"
|
||||
" };\n"
|
||||
" };\n";
|
||||
}
|
||||
s << " };\n";
|
||||
// For each memory
|
||||
uint64_t dmem_base = DMEM_BASE;
|
||||
uint64_t dmem_size = DMEM_SIZE;
|
||||
s << std::hex <<
|
||||
" memory@" << dmem_base << " {\n"
|
||||
" device_type = \"memory\";\n"
|
||||
" reg = <0x" << (dmem_base >> 32) << " 0x" << (dmem_base & (uint32_t)-1) <<
|
||||
" 0x" << (dmem_size >> 32) << " 0x" << (dmem_size & (uint32_t)-1) << ">;\n"
|
||||
" };\n";
|
||||
// System
|
||||
s << " soc {\n"
|
||||
" #address-cells = <2>;\n"
|
||||
" #size-cells = <2>;\n"
|
||||
" compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n"
|
||||
" ranges;\n"
|
||||
" clint@" << CLINT_BASE << " {\n"
|
||||
" compatible = \"riscv,clint0\";\n"
|
||||
" interrupts-extended = <" << std::dec;
|
||||
for (size_t i = 0; i < NUM_PROCS; i++)
|
||||
s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
|
||||
uint64_t clint_base = CLINT_BASE;
|
||||
uint64_t clint_size = CLINT_SIZE;
|
||||
s << std::hex << ">;\n"
|
||||
" reg = <0x" << (clint_base >> 32) << " 0x" << (clint_base & (uint32_t)-1) <<
|
||||
" 0x" << (clint_size >> 32) << " 0x" << (clint_size & (uint32_t)-1) << ">;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
/*
|
||||
" htif {\n"
|
||||
" compatible = \"ucb,htif0\";\n"
|
||||
" };\n"
|
||||
*/
|
||||
" uart@" << UART_BASE << " {\n"
|
||||
" compatible = \"ns16550a\";\n"
|
||||
" reg = <0x0 0x" << UART_BASE << " 0x0 0x8>;\n"
|
||||
" reg-shift = <3>;\n"
|
||||
" clock-frequency = <" << std::dec << CPU_HZ << std::hex << ">;\n"
|
||||
" };\n"
|
||||
"};\n";
|
||||
|
||||
dts = s.str();
|
||||
std::string dtb = dts_compile(dts);
|
||||
|
||||
//printf("%s\n\n", dts.c_str());
|
||||
|
||||
rom.insert(rom.end(), dtb.begin(), dtb.end());
|
||||
const int align = 0x1000;
|
||||
rom.resize((rom.size() + align - 1) / align * align);
|
||||
|
||||
// Print the contents
|
||||
int bytes_per_word = 4;
|
||||
printf ("@ 1000\n");
|
||||
for (int i = 0; i < rom.size(); i += bytes_per_word) {
|
||||
for (int j = bytes_per_word - 1; j >= 0; j--) {
|
||||
printf ("%02x", (unsigned char)(rom[i+j]));
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
if ((rom.size() % bytes_per_word) != 0)
|
||||
printf ("WARNING: last bytes of ROM omitted (incomplete word)\n");
|
||||
}
|
||||
|
||||
void usage(char *progname) {
|
||||
printf("usage: %s <xlen> <isa>\n", progname);
|
||||
printf("example: %s 64 imafdcus\n", progname);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *progname = argv[0];
|
||||
|
||||
if (argc != 3) {
|
||||
usage(progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int xlen;
|
||||
char *rv_str = argv[1];
|
||||
if (strcmp(rv_str,"RV32") == 0) {
|
||||
xlen = 32;
|
||||
} else if (strcmp(rv_str,"RV64") == 0) {
|
||||
xlen = 64;
|
||||
} else {
|
||||
usage(progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char isa_string[250];
|
||||
strcpy(isa_string, argv[2]);
|
||||
isa_string[249] = '\0';
|
||||
|
||||
make_dtb(xlen, isa_string);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
44
src_Testbench/SoC/External_Control.bsv
Normal file
44
src_Testbench/SoC/External_Control.bsv
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package External_Control;
|
||||
|
||||
// ================================================================
|
||||
// This package defines control request and response types from an
|
||||
// external agent (usually GDB) to the SoC.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// External control requests
|
||||
|
||||
typedef struct {
|
||||
Bit #(8) op;
|
||||
Bit #(64) arg1;
|
||||
Bit #(64) arg2;
|
||||
} Control_Req
|
||||
deriving (Bits, FShow);
|
||||
|
||||
// ----------------
|
||||
// Reads and writes to the Debug Module
|
||||
|
||||
Bit #(8) external_control_req_op_read_control_fabric = 10; // arg1: fabric_addr
|
||||
Bit #(8) external_control_req_op_write_control_fabric = 11; // arg1: fabric_addr, arg2: data
|
||||
|
||||
// ================================================================
|
||||
// External control responses
|
||||
|
||||
typedef struct {
|
||||
Bit #(8) status;
|
||||
Bit #(64) result;
|
||||
} Control_Rsp
|
||||
deriving (Bits, FShow);
|
||||
|
||||
Bit #(8) external_control_rsp_status_ok = 0;
|
||||
Bit #(8) external_control_rsp_status_err = 1;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
654
src_Testbench/SoC/Mem_Controller.bsv
Normal file
654
src_Testbench/SoC/Mem_Controller.bsv
Normal file
@@ -0,0 +1,654 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Mem_Controller;
|
||||
|
||||
// ================================================================
|
||||
// This module is a slave on the interconnect Fabric.
|
||||
//
|
||||
// On the back side of the Mem_Controller is a ``raw'' memory
|
||||
// interface, a simple, wide, R/W interface,
|
||||
// which is connected to real memory in hardware (BRAM, DRAM, ...)
|
||||
// and to a model thereof in simulation.
|
||||
//
|
||||
// The raw mem interface data width is typically one or two cache lines.
|
||||
// Note: raw mem write requests are 'fire and forget'; there is no ack
|
||||
|
||||
// ----------------
|
||||
// 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.
|
||||
|
||||
// When fabric data is 64b wide, fabric addresses must be 8B-aligned
|
||||
// - Reads always return 64b data
|
||||
// - Write data should be 64b wide with an 8b byte-strobe indicating
|
||||
// which bytes are to be written. Strobes should be for aligned
|
||||
// 1B, 2B, 4B or 8B chunks.
|
||||
|
||||
// When fabric data is 32b wide, fabric addresses must be 4B-aligned
|
||||
// - Reads always return 32b data
|
||||
// - Write data should be 32b wide with a 4b byte-strobe indicating
|
||||
// which bytes are to be written. Strobes should for aligned
|
||||
// 1B, 2B, or 4B chunks.
|
||||
|
||||
// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
|
||||
// necessary to satisfy type-checking.
|
||||
// ================================================================
|
||||
|
||||
export
|
||||
|
||||
Bits_per_Raw_Mem_Addr,
|
||||
Raw_Mem_Addr,
|
||||
|
||||
Bits_per_Raw_Mem_Word,
|
||||
Raw_Mem_Word,
|
||||
|
||||
Mem_Controller_IFC (..),
|
||||
mkMem_Controller;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import SpecialFIFOs :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Memory :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import ByteLane :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import Fabric_Defs :: *;
|
||||
import SoC_Map :: *;
|
||||
import AXI4_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// Raw mem data width: 256 (bits/ 32 x Byte/ 8 x Word32/ 4 x Word64)
|
||||
// Raw mem address width: 64 (arbitrarily chosen generously large)
|
||||
|
||||
typedef 256 Bits_per_Raw_Mem_Word;
|
||||
typedef Bit #(Bits_per_Raw_Mem_Word) Raw_Mem_Word;
|
||||
|
||||
typedef 64 Bits_per_Raw_Mem_Addr;
|
||||
typedef Bit #(Bits_per_Raw_Mem_Addr) Raw_Mem_Addr;
|
||||
|
||||
// ----------------
|
||||
// Views of raw mem word as bytes, word32s and word64s
|
||||
// Example values on the right based on 256b raw mem data width
|
||||
typedef TDiv #(Bits_per_Raw_Mem_Word, 8) Bytes_per_Raw_Mem_Word; // 32 bytes
|
||||
Integer bytes_per_raw_mem_word = valueOf (Bytes_per_Raw_Mem_Word);
|
||||
|
||||
// # of addr lsbs to index a byte in a Raw_Mem_Word
|
||||
typedef TLog #(Bytes_per_Raw_Mem_Word) Bits_per_Byte_in_Raw_Mem_Word; // 5
|
||||
Integer bits_per_byte_in_raw_mem_word = valueOf (Bits_per_Byte_in_Raw_Mem_Word);
|
||||
Integer hi_byte_in_raw_mem_word = bits_per_byte_in_raw_mem_word - 1; // 4
|
||||
|
||||
typedef TDiv #(Bits_per_Raw_Mem_Word, 32) Word32s_per_Raw_Mem_Word; // 8 x 32b words
|
||||
Integer word32s_per_raw_mem_word = valueOf (Word32s_per_Raw_Mem_Word);
|
||||
// # of addr lsbs to index a Word32 in a Raw_Mem_Word seen as a vector of Word32s
|
||||
typedef TLog #(Word32s_per_Raw_Mem_Word) Bits_per_Word32_in_Raw_Mem_Word; // 3
|
||||
// Type of index of a Word32 in a Raw_Mem_Word seen as a vector of Word32s
|
||||
typedef Bit #(Bits_per_Word32_in_Raw_Mem_Word) Word32_in_Raw_Mem_Word;
|
||||
|
||||
typedef TDiv #(Bits_per_Raw_Mem_Word, 64) Word64s_per_Raw_Mem_Word; // 4 x 64b words
|
||||
Integer word64s_per_raw_mem_word = valueOf (Word64s_per_Raw_Mem_Word);
|
||||
// # of addr lsbs to index a Word64 in a Raw_Mem_Word seen as a vector of Word64s
|
||||
typedef TLog #(Word64s_per_Raw_Mem_Word) Bits_per_Word64_in_Raw_Mem_Word; // 2
|
||||
// Type of index of a Word64 in a Raw_Mem_Word seen as a vector of Word64s
|
||||
typedef Bit #(Bits_per_Word64_in_Raw_Mem_Word) Word64_in_Raw_Mem_Word;
|
||||
|
||||
typedef TDiv #(Bytes_per_Raw_Mem_Word, Bytes_per_Fabric_Data) Fabric_Data_per_Raw_Mem_Word;
|
||||
|
||||
// Index of bit that selects a fabric data word in an address
|
||||
`ifdef FABRIC32
|
||||
Integer lo_fabric_data = 2;
|
||||
`endif
|
||||
|
||||
`ifdef FABRIC64
|
||||
Integer lo_fabric_data = 3;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
|
||||
function Bool fn_addr_is_aligned (Fabric_Addr addr, AXI4_Size size);
|
||||
Bool is_aligned = ( (size == axsize_1)
|
||||
|| ((size == axsize_2) && (addr [0] == 1'h0))
|
||||
|| ((size == axsize_4) && (addr [1:0] == 2'h0))
|
||||
|| ((size == axsize_8) && (addr [2:0] == 3'h0))
|
||||
|| ((size == axsize_16) && (addr [3:0] == 4'h0))
|
||||
|| ((size == axsize_32) && (addr [4:0] == 5'h0))
|
||||
|| ((size == axsize_64) && (addr [5:0] == 6'h0))
|
||||
|| ((size == axsize_128) && (addr [6:0] == 7'h0)));
|
||||
return is_aligned;
|
||||
endfunction
|
||||
|
||||
function Bool fn_addr_is_in_range (Fabric_Addr addr_base, Fabric_Addr addr, Fabric_Addr addr_lim);
|
||||
// Note: 'in_range' is redundant if the fabric only delivers
|
||||
// relevant addresses to this module so this is just a bit of
|
||||
// defensive programming.
|
||||
|
||||
return ((addr_base <= addr) && (addr < addr_lim));
|
||||
endfunction
|
||||
|
||||
function Bool fn_addr_is_ok (Fabric_Addr addr_base, Fabric_Addr addr, Fabric_Addr addr_lim, AXI4_Size size);
|
||||
return ( fn_addr_is_aligned (addr, size)
|
||||
&& fn_addr_is_in_range (addr_base, addr, addr_lim));
|
||||
endfunction
|
||||
|
||||
// Compute raw mem addr that holds a given fabric addr
|
||||
function Raw_Mem_Addr fn_addr_to_raw_mem_addr (Fabric_Addr addr);
|
||||
Fabric_Addr a1 = addr >> log2 (bytes_per_raw_mem_word);
|
||||
return extend (a1);
|
||||
endfunction
|
||||
|
||||
// Compute # of raw mem words from base to lim fabric addrs
|
||||
function Raw_Mem_Addr fn_raw_mem_words_per_mem (Fabric_Addr base, Fabric_Addr lim);
|
||||
return fn_addr_to_raw_mem_addr (lim - base);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Local constants and types
|
||||
|
||||
// Module state
|
||||
typedef enum {STATE_POWER_ON_RESET,
|
||||
`ifdef INCLUDE_INITIAL_MEMZERO
|
||||
STATE_ZEROING_MEM, // while zero-ing out memory
|
||||
`endif
|
||||
STATE_RESET_RELOAD_CACHE, // on reset, start reload on reset
|
||||
STATE_RELOADING, // while reloading the raw-mem word cache
|
||||
STATE_READY // while handling requests
|
||||
} State
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface Mem_Controller_IFC;
|
||||
// Reset
|
||||
interface Server #(Bit #(0), Bit #(0)) server_reset;
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
|
||||
// To raw memory (outside the SoC)
|
||||
interface MemoryClient #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word) to_raw_mem;
|
||||
|
||||
// For ISA tests: watch memory writes to <tohost> addr
|
||||
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// AXI4 has independent read and write channels and does not specify
|
||||
// which one should be prioritized if requests are available on both
|
||||
// channels. We merge them into a single queue.
|
||||
|
||||
typedef enum { REQ_OP_RD, REQ_OP_WR } Req_Op
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {Req_Op req_op;
|
||||
|
||||
// AW and AR channel info
|
||||
Fabric_Id id;
|
||||
Fabric_Addr addr;
|
||||
AXI4_Len len;
|
||||
AXI4_Size size;
|
||||
AXI4_Burst burst;
|
||||
AXI4_Lock lock;
|
||||
AXI4_Cache cache;
|
||||
AXI4_Prot prot;
|
||||
AXI4_QoS qos;
|
||||
AXI4_Region region;
|
||||
Bit #(Wd_User) user;
|
||||
|
||||
// Write data info
|
||||
Bit #(TDiv #(Wd_Data, 8)) wstrb;
|
||||
Fabric_Data data;
|
||||
} Req
|
||||
deriving (Bits, FShow);
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkMem_Controller (Mem_Controller_IFC);
|
||||
|
||||
// verbosity 0: quiet
|
||||
// verbosity 1: reset, initialized
|
||||
// verbosity 2: reads, writes
|
||||
// verbosity 3: more detail of local raw_mem interactions
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
Reg #(State) rg_state <- mkReg (STATE_POWER_ON_RESET);
|
||||
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
|
||||
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
|
||||
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// Communication with fabric
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// Requests merged from the (WrA, WrD) and RdA channels
|
||||
FIFOF #(Req) f_reqs <- mkPipelineFIFOF;
|
||||
|
||||
// FIFOFs for requests/responses to raw memory
|
||||
FIFOF #(MemoryRequest #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word))
|
||||
f_raw_mem_reqs <- mkPipelineFIFOF;
|
||||
FIFOF #(MemoryResponse #(Bits_per_Raw_Mem_Word))
|
||||
f_raw_mem_rsps <- mkPipelineFIFOF;
|
||||
|
||||
// We maintain a 1-raw_mem_word cache
|
||||
Reg #(Bool) rg_cached_clean <- mkRegU;
|
||||
Reg #(Raw_Mem_Addr) rg_cached_raw_mem_addr <- mkRegU;
|
||||
Reg #(Raw_Mem_Word) rg_cached_raw_mem_word <- mkRegU;
|
||||
|
||||
// Ad hoc ISA-test simulation support: watch <tohost> and stop on non-zero write.
|
||||
// The default tohost_addr here is fragile (may change on recompilation of tests).
|
||||
// Proper value can be provided with 'set_watch_tohost' method from symbol table
|
||||
Reg #(Bool) rg_watch_tohost <- mkReg (False);
|
||||
Reg #(Fabric_Addr) rg_tohost_addr <- mkReg ('h_8000_1000);
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Reset
|
||||
|
||||
function Action fa_reset_actions;
|
||||
action
|
||||
slave_xactor.reset;
|
||||
f_raw_mem_reqs.clear;
|
||||
f_raw_mem_rsps.clear;
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule rl_power_on_reset (rg_state == STATE_POWER_ON_RESET);
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Mem_Controller.rl_power_on_reset", cur_cycle);
|
||||
fa_reset_actions ();
|
||||
rg_state <= STATE_RESET_RELOAD_CACHE;
|
||||
endrule
|
||||
|
||||
rule rl_external_reset (rg_state == STATE_READY);
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Mem_Controller.rl_external_reset => STATE_RESET_RELOAD_CACHE", cur_cycle);
|
||||
|
||||
f_reset_reqs.deq;
|
||||
fa_reset_actions ();
|
||||
rg_state <= STATE_RESET_RELOAD_CACHE;
|
||||
f_reset_rsps.enq (?);
|
||||
endrule
|
||||
|
||||
// On reset, we initialize the local cache with contents of raw_mem_addr 0
|
||||
rule rl_reset_reload_cache (rg_state == STATE_RESET_RELOAD_CACHE);
|
||||
let raw_mem_req = MemoryRequest {write: False,
|
||||
byteen: '1,
|
||||
address: 0,
|
||||
data: ?};
|
||||
f_raw_mem_reqs.enq (raw_mem_req);
|
||||
rg_cached_raw_mem_addr <= 0;
|
||||
rg_state <= STATE_RELOADING;
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Mem_Controller.rl_reset_reload_cache => STATE_RELOADING", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Merge requests into a single queue, prioritizing reads over writes
|
||||
|
||||
rule rl_merge_rd_req;
|
||||
let rda <- pop_o (slave_xactor.o_rd_addr);
|
||||
let req = Req {req_op: REQ_OP_RD,
|
||||
id: rda.arid,
|
||||
addr: rda.araddr,
|
||||
len: rda.arlen,
|
||||
size: rda.arsize,
|
||||
burst: rda.arburst,
|
||||
lock: rda.arlock,
|
||||
cache: rda.arcache,
|
||||
prot: rda.arprot,
|
||||
qos: rda.arqos,
|
||||
region: rda.arregion,
|
||||
user: rda.aruser,
|
||||
wstrb: ?,
|
||||
data: ?};
|
||||
f_reqs.enq (req);
|
||||
|
||||
if (cfg_verbosity > 2) begin
|
||||
$display ("%0d: Mem_Controller.rl_merge_rd_req", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "rl_merge_rd_req, rl_merge_wr_req" *)
|
||||
rule rl_merge_wr_req;
|
||||
let wra <- pop_o (slave_xactor.o_wr_addr);
|
||||
let wrd <- pop_o (slave_xactor.o_wr_data);
|
||||
let req = Req {req_op: REQ_OP_WR,
|
||||
id: wra.awid,
|
||||
addr: wra.awaddr,
|
||||
len: wra.awlen,
|
||||
size: wra.awsize,
|
||||
burst: wra.awburst,
|
||||
lock: wra.awlock,
|
||||
cache: wra.awcache,
|
||||
prot: wra.awprot,
|
||||
qos: wra.awqos,
|
||||
region: wra.awregion,
|
||||
user: wra.awuser,
|
||||
wstrb: wrd.wstrb,
|
||||
data: wrd.wdata};
|
||||
f_reqs.enq (req);
|
||||
|
||||
if (cfg_verbosity > 2) begin
|
||||
$display ("%0d: Mem_Controller.rl_merge_wr_req", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle request from fabric
|
||||
|
||||
let req_byte_offset = f_reqs.first.addr - rg_addr_base; // within this memory unit
|
||||
let req_raw_mem_addr = fn_addr_to_raw_mem_addr (req_byte_offset);
|
||||
|
||||
// ----------------
|
||||
// This rule fires when there's no fabric req and the cached raw_mem_word is dirty;
|
||||
// it writes back the dirty raw_mem_word; the cached raw_mem_word becomes clean
|
||||
|
||||
rule rl_writeback_dirty_idle ( (rg_state == STATE_READY)
|
||||
&& (! f_reqs.notEmpty) // Idle
|
||||
&& (! rg_cached_clean));
|
||||
let raw_mem_req = MemoryRequest {write: True,
|
||||
byteen: '1,
|
||||
address: rg_cached_raw_mem_addr,
|
||||
data: rg_cached_raw_mem_word};
|
||||
f_raw_mem_reqs.enq (raw_mem_req);
|
||||
rg_cached_clean <= True;
|
||||
if (cfg_verbosity > 2)
|
||||
$display ("%0d: Mem_Controller.rl_writeback_dirty_idle to raw addr 0x%0h",
|
||||
cur_cycle, rg_cached_raw_mem_addr);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// This rule fires on a fabric req when the cached raw_mem_word has a
|
||||
// different raw_mem_word-addr and is dirty;
|
||||
// it writes back the dirty raw_mem_word; the cached raw_mem_word becomes clean
|
||||
|
||||
rule rl_writeback_dirty ( (rg_state == STATE_READY)
|
||||
&& fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size)
|
||||
&& (rg_cached_raw_mem_addr != req_raw_mem_addr)
|
||||
&& (! rg_cached_clean));
|
||||
let raw_mem_req = MemoryRequest {write: True,
|
||||
byteen: '1,
|
||||
address: rg_cached_raw_mem_addr,
|
||||
data: rg_cached_raw_mem_word};
|
||||
f_raw_mem_reqs.enq (raw_mem_req);
|
||||
rg_cached_clean <= True;
|
||||
if (cfg_verbosity > 2)
|
||||
$display ("%0d: Mem_Controller.rl_writeback_dirty to raw addr 0x%0h",
|
||||
cur_cycle, rg_cached_raw_mem_addr);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// This rule fires on a fabric req when the cached raw_mem_word has a
|
||||
// different addr and is clean; we overwrite with the correct raw_mem_word
|
||||
// by reloading from memory; the new cached raw_mem_word is clean.
|
||||
|
||||
rule rl_miss_clean_req ( (rg_state == STATE_READY)
|
||||
&& fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size)
|
||||
&& (rg_cached_raw_mem_addr != req_raw_mem_addr)
|
||||
&& rg_cached_clean);
|
||||
let raw_mem_req = MemoryRequest {write: False,
|
||||
byteen: '1,
|
||||
address: req_raw_mem_addr,
|
||||
data: ?};
|
||||
f_raw_mem_reqs.enq (raw_mem_req);
|
||||
rg_cached_raw_mem_addr <= req_raw_mem_addr;
|
||||
rg_state <= STATE_RELOADING;
|
||||
|
||||
if (cfg_verbosity > 2)
|
||||
$display ("%0d: Mem_Controller.rl_miss_clean_req: read raw addr 0x%0h",
|
||||
cur_cycle, req_raw_mem_addr);
|
||||
endrule
|
||||
|
||||
rule rl_reload (rg_state == STATE_RELOADING);
|
||||
let raw_mem_rsp <- pop (f_raw_mem_rsps);
|
||||
Raw_Mem_Word raw_mem_word = unpack (raw_mem_rsp.data);
|
||||
rg_cached_raw_mem_word <= raw_mem_word;
|
||||
rg_state <= STATE_READY;
|
||||
rg_cached_clean <= True;
|
||||
|
||||
if (cfg_verbosity > 2) begin
|
||||
$display ("%0d: Mem_Controller.rl_reload: raw addr 0x%0h", cur_cycle, rg_cached_raw_mem_addr);
|
||||
$display (" ", fshow (raw_mem_word));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// This rule fires on a fabric read request when the cached raw_mem_word has the
|
||||
// same addr ('hit'), whether clean or dirty.
|
||||
// Returns the full Wd_Data-wide word containing the byte specified by the address.
|
||||
// i.e., we do not extract relevant bytes here, leaving that to the requestor.
|
||||
|
||||
rule rl_process_rd_req ( (rg_state == STATE_READY)
|
||||
&& fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size)
|
||||
&& (rg_cached_raw_mem_addr == req_raw_mem_addr)
|
||||
&& (f_reqs.first.req_op == REQ_OP_RD));
|
||||
|
||||
// ----------------
|
||||
// We need to select the fabric data word from the raw mem word that contains the target address.
|
||||
|
||||
// View the raw mem word as a vector of fabric data words (Wd_Data width words)
|
||||
Vector #(Fabric_Data_per_Raw_Mem_Word, Bit #(Wd_Data)) raw_mem_word_V_fabric_data = unpack (rg_cached_raw_mem_word);
|
||||
|
||||
// Get the index into this vector of the fabric word containing the target address.
|
||||
// For this index, use a generous size (here Bit #(16)), and let zeroExtend pad it automaticallly.
|
||||
Fabric_Addr addr = f_reqs.first.addr;
|
||||
Bit #(Bits_per_Byte_in_Raw_Mem_Word) n = addr [hi_byte_in_raw_mem_word : 0];
|
||||
n = (n >> lo_fabric_data);
|
||||
|
||||
// Select the fabric data word of interest
|
||||
Bit #(Wd_Data) rdata = raw_mem_word_V_fabric_data [n];
|
||||
|
||||
let rdr = AXI4_Rd_Data {rid: f_reqs.first.id,
|
||||
rdata: rdata,
|
||||
rresp: axi4_resp_okay,
|
||||
rlast: True,
|
||||
ruser: f_reqs.first.user};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
f_reqs.deq;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: Mem_Controller.rl_process_rd_req: ", cur_cycle);
|
||||
$display (" ", fshow (f_reqs.first));
|
||||
$display (" => ", fshow (rdr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// This rule fires on a fabric write request when the cached raw_mem_word has the
|
||||
// same addr ('hit'), whether clean or dirty.
|
||||
|
||||
rule rl_process_wr_req ( (rg_state == STATE_READY)
|
||||
&& fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size)
|
||||
&& (rg_cached_raw_mem_addr == req_raw_mem_addr)
|
||||
&& (f_reqs.first.req_op == REQ_OP_WR));
|
||||
// Get the old (cached) value of the word64
|
||||
Word64_in_Raw_Mem_Word word64_in_raw_mem_word = f_reqs.first.addr [hi_byte_in_raw_mem_word : 3];
|
||||
Vector #(Word64s_per_Raw_Mem_Word, Bit #(64)) raw_mem_word_V_Word64 = unpack (rg_cached_raw_mem_word);
|
||||
Bit #(64) word64_old = raw_mem_word_V_Word64 [word64_in_raw_mem_word];
|
||||
|
||||
// Lane-adjust the new word64
|
||||
Bit #(64) word64_new = zeroExtend (f_reqs.first.data);
|
||||
Bit #(8) strobe = zeroExtend (f_reqs.first.wstrb);
|
||||
if ((valueOf (Wd_Data) == 32) && (f_reqs.first.addr [2] == 1'b1)) begin
|
||||
// Upper 32b only
|
||||
word64_new = { word64_new [31:0], 0 };
|
||||
strobe = { strobe [3:0], 0 };
|
||||
end
|
||||
Bit #(64) mask = fn_strobe_to_mask (strobe);
|
||||
let updated_word64 = ((word64_old & (~ mask)) | (word64_new & mask));
|
||||
|
||||
// Write it back into the cached raw_mem_word
|
||||
raw_mem_word_V_Word64 [word64_in_raw_mem_word] = updated_word64;
|
||||
rg_cached_raw_mem_word <= pack (raw_mem_word_V_Word64);
|
||||
rg_cached_clean <= False;
|
||||
|
||||
let wrr = AXI4_Wr_Resp {bid: f_reqs.first.id,
|
||||
bresp: axi4_resp_okay,
|
||||
buser: f_reqs.first.user};
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
f_reqs.deq;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: Mem_Controller.rl_process_wr_req: ", cur_cycle);
|
||||
$display (" ", fshow (f_reqs.first));
|
||||
$display (" => ", fshow (wrr));
|
||||
end
|
||||
|
||||
// For simulation testing of riscv-tests/isa only:
|
||||
if ((rg_watch_tohost)
|
||||
&& (f_reqs.first.addr == rg_tohost_addr)
|
||||
&& (word64_new != 0))
|
||||
begin
|
||||
|
||||
$display ("%0d: Mem_Controller.rl_process_wr_req: addr 0x%0h (<tohost>) data 0x%0h",
|
||||
cur_cycle, f_reqs.first.addr, word64_new);
|
||||
let exit_value = (word64_new >> 1);
|
||||
if (exit_value == 0)
|
||||
$display ("PASS");
|
||||
else
|
||||
$display ("FAIL %0d", exit_value);
|
||||
$finish (truncate (exit_value));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Zero-memory FSM: zero out memory.
|
||||
// If needed, we must provide a way to enable it from the debug_module
|
||||
// This rule should be enabled with:
|
||||
// rg_cached_raw_mem_addr <= 0;
|
||||
// rg_state <= STATE_ZEROING_MEM;
|
||||
|
||||
`ifdef INCLUDE_INITIAL_MEMZERO
|
||||
rule rl_zero_mem (rg_state == STATE_ZEROING_MEM);
|
||||
let raw_mem_req = MemoryRequest {write: True,
|
||||
byteen: '1,
|
||||
address: rg_cached_raw_mem_addr,
|
||||
data: 0};
|
||||
f_raw_mem_reqs.enq (raw_mem_req);
|
||||
|
||||
// Last write
|
||||
let raw_mem_words_per_mem = fn_raw_mem_words_per_mem (rg_addr_base, rg_addr_lim);
|
||||
if (rg_cached_raw_mem_addr == (raw_mem_words_per_mem - 1)) begin
|
||||
rg_cached_raw_mem_addr <= rg_cached_raw_mem_addr;
|
||||
rg_cached_raw_mem_word <= unpack (0);
|
||||
rg_cached_clean <= True;
|
||||
rg_state <= STATE_READY;
|
||||
|
||||
// if (cfg_verbosity != 0)
|
||||
$display ("%0d: Mem_Controller: zeroed %0d raw-memory locations (%0d-bit words)",
|
||||
cur_cycle, raw_mem_words_per_mem, valueOf (Bits_per_Raw_Mem_Word));
|
||||
end
|
||||
else
|
||||
rg_cached_raw_mem_addr <= rg_cached_raw_mem_addr + 1;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Invalid address
|
||||
|
||||
rule rl_invalid_rd_address ( (rg_state == STATE_READY)
|
||||
&& (! fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size))
|
||||
&& (f_reqs.first.req_op == REQ_OP_RD));
|
||||
Fabric_Data rdata = zeroExtend (f_reqs.first.addr);
|
||||
let rdr = AXI4_Rd_Data {rid: f_reqs.first.id,
|
||||
rdata: rdata, // for debugging only
|
||||
rresp: axi4_resp_slverr,
|
||||
rlast: True,
|
||||
ruser: f_reqs.first.user};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
f_reqs.deq;
|
||||
|
||||
$write ("%0d: ERROR: Mem_Controller:", cur_cycle);
|
||||
if (! fn_addr_is_aligned (f_reqs.first.addr, f_reqs.first.size))
|
||||
$display (" read-addr is misaligned");
|
||||
else
|
||||
$display (" read-addr is out of bounds");
|
||||
$display (" rg_addr_base 0x%0h rg_addr_lim 0x%0h", rg_addr_base, rg_addr_lim);
|
||||
$display (" ", fshow (f_reqs.first));
|
||||
$display (" => ", fshow (rdr));
|
||||
endrule
|
||||
|
||||
rule rl_invalid_wr_address ( (rg_state == STATE_READY)
|
||||
&& (! fn_addr_is_ok (rg_addr_base, f_reqs.first.addr, rg_addr_lim, f_reqs.first.size))
|
||||
&& (f_reqs.first.req_op == REQ_OP_WR));
|
||||
let wrr = AXI4_Wr_Resp {bid: f_reqs.first.id,
|
||||
bresp: axi4_resp_slverr,
|
||||
buser: f_reqs.first.user};
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
f_reqs.deq;
|
||||
|
||||
$write ("%0d: ERROR: Mem_Controller:", cur_cycle);
|
||||
if (! fn_addr_is_aligned (f_reqs.first.addr, f_reqs.first.size))
|
||||
$display (" write-addr is misaligned");
|
||||
else
|
||||
$display (" write-addr is out of bounds");
|
||||
$display (" rg_addr_base 0x%0h rg_addr_lim 0x%0h", rg_addr_base, rg_addr_lim);
|
||||
$display (" ", fshow (f_reqs.first));
|
||||
$display (" => ", fshow (wrr));
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim) if (rg_state == STATE_READY);
|
||||
rg_addr_base <= addr_base;
|
||||
rg_addr_lim <= addr_lim;
|
||||
$display ("%0d: Mem_Controller.set_addr_map: addr_base 0x%0h addr_lim 0x%0h",
|
||||
cur_cycle, addr_base, addr_lim);
|
||||
|
||||
`ifdef INCLUDE_INITIAL_MEMZERO
|
||||
rg_cached_raw_mem_addr <= 0;
|
||||
rg_state <= STATE_ZEROING_MEM;
|
||||
$display ("%0d: Mem_Controller.set_addr_map: zeroing memory from 0x%0h to 0x%0h",
|
||||
cur_cycle, addr_base, addr_lim);
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface slave = slave_xactor.axi_side;
|
||||
|
||||
// To raw memory (outside the SoC)
|
||||
interface to_raw_mem = toGPClient (f_raw_mem_reqs, f_raw_mem_rsps);
|
||||
|
||||
// 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;
|
||||
rg_tohost_addr <= tohost_addr;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
154
src_Testbench/SoC/SoC_Fabric.bsv
Normal file
154
src_Testbench/SoC/SoC_Fabric.bsv
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package SoC_Fabric;
|
||||
|
||||
// ================================================================
|
||||
// Defines a SoC Fabric that is a specialization of AXI4_Lite_Fabric
|
||||
// for this particular SoC.
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
|
||||
import Fabric_Defs :: *; // for Wd_Addr, Wd_Data, Wd_User
|
||||
import SoC_Map :: *; // for Num_Masters, Num_Slaves
|
||||
|
||||
// ================================================================
|
||||
// Slave address decoder
|
||||
// Identifies whether a given addr is legal and, if so, which slave services it.
|
||||
|
||||
typedef Bit #(TLog #(Num_Slaves)) Slave_Num;
|
||||
|
||||
// ================================================================
|
||||
// Specialization of parameterized AXI4 fabric for this SoC.
|
||||
|
||||
typedef AXI4_Fabric_IFC #(Num_Masters,
|
||||
Num_Slaves,
|
||||
Wd_Id,
|
||||
Wd_Addr,
|
||||
Wd_Data,
|
||||
Wd_User) Fabric_IFC;
|
||||
|
||||
// ----------------
|
||||
|
||||
(* synthesize *)
|
||||
module mkFabric (Fabric_IFC);
|
||||
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
function Tuple2 #(Bool, Slave_Num) fn_addr_to_slave_num (Fabric_Addr addr);
|
||||
|
||||
// Main Mem
|
||||
if ( (soc_map.m_mem0_controller_addr_base <= addr)
|
||||
&& (addr < soc_map.m_mem0_controller_addr_lim))
|
||||
return tuple2 (True, fromInteger (mem0_controller_slave_num));
|
||||
|
||||
// Boot ROM
|
||||
else if ( (soc_map.m_boot_rom_addr_base <= addr)
|
||||
&& (addr < soc_map.m_boot_rom_addr_lim))
|
||||
return tuple2 (True, fromInteger (boot_rom_slave_num));
|
||||
|
||||
`ifdef Near_Mem_TCM
|
||||
// TCM
|
||||
else if ( (soc_map.m_tcm_addr_base <= addr)
|
||||
&& (addr < soc_map.m_tcm_addr_lim))
|
||||
return tuple2 (True, fromInteger (tcm_back_door_slave_num));
|
||||
`endif
|
||||
|
||||
// UART
|
||||
else if ( (soc_map.m_uart0_addr_base <= addr)
|
||||
&& (addr < soc_map.m_uart0_addr_lim))
|
||||
return tuple2 (True, fromInteger (uart0_slave_num));
|
||||
|
||||
`ifdef HTIF_MEMORY
|
||||
else if ( (soc_map.m_htif_addr_base <= addr)
|
||||
&& (addr < soc_map.m_htif_addr_lim))
|
||||
return tuple2 (True, fromInteger (htif_slave_num));
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_ACCEL0
|
||||
// Accelerator 0
|
||||
else if ( (soc_map.m_accel0_addr_base <= addr)
|
||||
&& (addr < soc_map.m_accel0_addr_lim))
|
||||
return tuple2 (True, fromInteger (accel0_slave_num));
|
||||
`endif
|
||||
|
||||
else
|
||||
return tuple2 (False, ?);
|
||||
endfunction
|
||||
|
||||
AXI4_Fabric_IFC #(Num_Masters, Num_Slaves, Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
fabric <- mkAXI4_Fabric (fn_addr_to_slave_num);
|
||||
|
||||
return fabric;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
// Specialization of parameterized AXI4 fabric for this SoC.
|
||||
|
||||
typedef AXI4_Fabric_IFC #(Num_Masters,
|
||||
Num_Slaves,
|
||||
Wd_Id,
|
||||
Wd_Addr,
|
||||
Wd_Data,
|
||||
Wd_User) Fabric_AXI4_IFC;
|
||||
|
||||
// ----------------
|
||||
|
||||
(* synthesize *)
|
||||
module mkFabric_AXI4 (Fabric_AXI4_IFC);
|
||||
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
function Tuple2 #(Bool, Slave_Num) fn_addr_to_slave_num (Fabric_Addr addr);
|
||||
|
||||
// Main Mem
|
||||
if ( (soc_map.m_mem0_controller_addr_base <= addr)
|
||||
&& (addr < soc_map.m_mem0_controller_addr_lim))
|
||||
return tuple2 (True, fromInteger (mem0_controller_slave_num));
|
||||
|
||||
// Boot ROM
|
||||
else if ( (soc_map.m_boot_rom_addr_base <= addr)
|
||||
&& (addr < soc_map.m_boot_rom_addr_lim))
|
||||
return tuple2 (True, fromInteger (boot_rom_slave_num));
|
||||
|
||||
`ifdef Near_Mem_TCM
|
||||
// TCM
|
||||
else if ( (soc_map.m_tcm_addr_base <= addr)
|
||||
&& (addr < soc_map.m_tcm_addr_lim))
|
||||
return tuple2 (True, fromInteger (tcm_back_door_slave_num));
|
||||
`endif
|
||||
|
||||
// UART
|
||||
else if ( (soc_map.m_uart0_addr_base <= addr)
|
||||
&& (addr < soc_map.m_uart0_addr_lim))
|
||||
return tuple2 (True, fromInteger (uart0_slave_num));
|
||||
|
||||
`ifdef HTIF_MEMORY
|
||||
else if ( (soc_map.m_htif_addr_base <= addr)
|
||||
&& (addr < soc_map.m_htif_addr_lim))
|
||||
return tuple2 (True, fromInteger (htif_slave_num));
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_ACCEL0
|
||||
// Accelerator 0
|
||||
else if ( (soc_map.m_accel0_addr_base <= addr)
|
||||
&& (addr < soc_map.m_accel0_addr_lim))
|
||||
return tuple2 (True, fromInteger (accel0_slave_num));
|
||||
`endif
|
||||
|
||||
else
|
||||
return tuple2 (False, ?);
|
||||
endfunction
|
||||
|
||||
AXI4_Fabric_IFC #(Num_Masters, Num_Slaves, Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
fabric <- mkAXI4_Fabric (fn_addr_to_slave_num);
|
||||
|
||||
return fabric;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
273
src_Testbench/SoC/SoC_Map.bsv
Normal file
273
src_Testbench/SoC/SoC_Map.bsv
Normal file
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package SoC_Map;
|
||||
|
||||
// ================================================================
|
||||
// This module defines the overall 'address map' of the SoC, showing
|
||||
// the addresses serviced by each slave IP, and which addresses are
|
||||
// memory vs. I/O.
|
||||
|
||||
// ***** WARNING! WARNING! WARNING! *****
|
||||
|
||||
// During system integration, this address map should be identical to
|
||||
// the system interconnect settings (e.g., routing of requests between
|
||||
// masters and slaves). This map is also needed by software so that
|
||||
// it knows how to address various IPs.
|
||||
|
||||
// This module contains no state; it just has constants, and so can be
|
||||
// freely instantiated at multiple places in the SoC module hierarchy
|
||||
// at no hardware cost. It allows this map to be defined in one
|
||||
// place and shared across the SoC.
|
||||
|
||||
// ================================================================
|
||||
// Exports
|
||||
|
||||
export SoC_Map_IFC (..), mkSoC_Map;
|
||||
|
||||
// export fn_addr_in_range;
|
||||
|
||||
export Num_Masters;
|
||||
export imem_master_num;
|
||||
export dmem_master_num;
|
||||
|
||||
export Num_Slaves;
|
||||
export boot_rom_slave_num;
|
||||
export mem0_controller_slave_num;
|
||||
export uart0_slave_num;
|
||||
|
||||
export N_External_Interrupt_Sources;
|
||||
export n_external_interrupt_sources;
|
||||
export irq_num_uart0;
|
||||
|
||||
// ================================================================
|
||||
// Bluespec library imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import Fabric_Defs :: *; // Only for type Fabric_Addr
|
||||
|
||||
// ================================================================
|
||||
// Interface and module for the address map
|
||||
|
||||
interface SoC_Map_IFC;
|
||||
(* always_ready *) method Fabric_Addr m_near_mem_io_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_near_mem_io_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_near_mem_io_addr_lim;
|
||||
|
||||
(* always_ready *) method Fabric_Addr m_plic_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_plic_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_plic_addr_lim;
|
||||
|
||||
(* always_ready *) method Fabric_Addr m_uart0_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_uart0_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_uart0_addr_lim;
|
||||
|
||||
(* always_ready *) method Fabric_Addr m_boot_rom_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_boot_rom_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_boot_rom_addr_lim;
|
||||
|
||||
(* always_ready *) method Fabric_Addr m_mem0_controller_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_mem0_controller_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_mem0_controller_addr_lim;
|
||||
|
||||
(* always_ready *) method Fabric_Addr m_tcm_addr_base;
|
||||
(* always_ready *) method Fabric_Addr m_tcm_addr_size;
|
||||
(* always_ready *) method Fabric_Addr m_tcm_addr_lim;
|
||||
|
||||
(* always_ready *)
|
||||
method Bool m_is_mem_addr (Fabric_Addr addr);
|
||||
|
||||
(* always_ready *)
|
||||
method Bool m_is_IO_addr (Fabric_Addr addr);
|
||||
|
||||
(* always_ready *)
|
||||
method Bool m_is_near_mem_IO_addr (Fabric_Addr addr);
|
||||
|
||||
(* always_ready *) method Bit #(64) m_pc_reset_value;
|
||||
(* always_ready *) method Bit #(64) m_mtvec_reset_value;
|
||||
(* always_ready *) method Bit #(64) m_nmivec_reset_value;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkSoC_Map (SoC_Map_IFC);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Near_Mem_IO (including CLINT, the core-local interruptor)
|
||||
|
||||
Fabric_Addr near_mem_io_addr_base = 'h_0200_0000;
|
||||
Fabric_Addr near_mem_io_addr_size = 'h_0000_C000; // 48K
|
||||
Fabric_Addr near_mem_io_addr_lim = near_mem_io_addr_base + near_mem_io_addr_size;
|
||||
|
||||
function Bool fn_is_near_mem_io_addr (Fabric_Addr addr);
|
||||
return ((near_mem_io_addr_base <= addr) && (addr < near_mem_io_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// PLIC
|
||||
|
||||
Fabric_Addr plic_addr_base = 'h_0C00_0000;
|
||||
Fabric_Addr plic_addr_size = 'h_0040_0000; // 4M
|
||||
Fabric_Addr plic_addr_lim = plic_addr_base + plic_addr_size;
|
||||
|
||||
function Bool fn_is_plic_addr (Fabric_Addr addr);
|
||||
return ((plic_addr_base <= addr) && (addr < plic_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// UART 0
|
||||
|
||||
Fabric_Addr uart0_addr_base = 'hC000_0000;
|
||||
Fabric_Addr uart0_addr_size = 'h0000_0080; // 128
|
||||
Fabric_Addr uart0_addr_lim = uart0_addr_base + uart0_addr_size;
|
||||
|
||||
function Bool fn_is_uart0_addr (Fabric_Addr addr);
|
||||
return ((uart0_addr_base <= addr) && (addr < uart0_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Boot ROM
|
||||
|
||||
Fabric_Addr boot_rom_addr_base = 'h_0000_1000;
|
||||
Fabric_Addr boot_rom_addr_size = 'h_0000_1000; // 4K
|
||||
Fabric_Addr boot_rom_addr_lim = boot_rom_addr_base + boot_rom_addr_size;
|
||||
|
||||
function Bool fn_is_boot_rom_addr (Fabric_Addr addr);
|
||||
return ((boot_rom_addr_base <= addr) && (addr < boot_rom_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Main Mem Controller 0
|
||||
|
||||
Fabric_Addr mem0_controller_addr_base = 'h_8000_0000;
|
||||
Fabric_Addr mem0_controller_addr_size = 'h_1000_0000; // 256 MB
|
||||
Fabric_Addr mem0_controller_addr_lim = mem0_controller_addr_base + mem0_controller_addr_size;
|
||||
|
||||
function Bool fn_is_mem0_controller_addr (Fabric_Addr addr);
|
||||
return ((mem0_controller_addr_base <= addr) && (addr < mem0_controller_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Tightly-coupled memory ('TCM'; optional)
|
||||
|
||||
`ifdef Near_Mem_TCM
|
||||
// Integer kB_per_TCM = 'h4; // 4KB
|
||||
// Integer kB_per_TCM = 'h40; // 64KB
|
||||
// Integer kB_per_TCM = 'h80; // 128KB
|
||||
// Integer kB_per_TCM = 'h400; // 1 MB
|
||||
Integer kB_per_TCM = 'h4000; // 16 MB
|
||||
`else
|
||||
Integer kB_per_TCM = 0;
|
||||
`endif
|
||||
Integer bytes_per_TCM = kB_per_TCM * 'h400;
|
||||
|
||||
Fabric_Addr tcm_addr_base = 'h_0000_0000;
|
||||
Fabric_Addr tcm_addr_size = fromInteger (bytes_per_TCM);
|
||||
Fabric_Addr tcm_addr_lim = tcm_addr_base + tcm_addr_size;
|
||||
|
||||
function Bool fn_is_tcm_addr (Fabric_Addr addr);
|
||||
return ((tcm_addr_base <= addr) && (addr < tcm_addr_lim));
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Memory address predicate
|
||||
// Identifies memory addresses in the Fabric.
|
||||
// (Caches need this information to cache these addresses.)
|
||||
|
||||
function Bool fn_is_mem_addr (Fabric_Addr addr);
|
||||
return ( fn_is_boot_rom_addr (addr)
|
||||
|| fn_is_mem0_controller_addr (addr)
|
||||
|| fn_is_tcm_addr (addr)
|
||||
);
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// I/O address predicate
|
||||
// Identifies I/O addresses in the Fabric.
|
||||
// (Caches need this information to avoid cacheing these addresses.)
|
||||
|
||||
function Bool fn_is_IO_addr (Fabric_Addr addr);
|
||||
return ( fn_is_near_mem_io_addr (addr)
|
||||
|| fn_is_plic_addr (addr)
|
||||
|| fn_is_uart0_addr (addr)
|
||||
);
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// PC, MTVEC and NMIVEC reset values
|
||||
|
||||
Bit #(64) pc_reset_value = boot_rom_addr_base;
|
||||
Bit #(64) mtvec_reset_value = 'h1000; // TODO
|
||||
Bit #(64) nmivec_reset_value = ?; // TODO
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Fabric_Addr m_near_mem_io_addr_base = near_mem_io_addr_base;
|
||||
method Fabric_Addr m_near_mem_io_addr_size = near_mem_io_addr_size;
|
||||
method Fabric_Addr m_near_mem_io_addr_lim = near_mem_io_addr_lim;
|
||||
|
||||
method Fabric_Addr m_plic_addr_base = plic_addr_base;
|
||||
method Fabric_Addr m_plic_addr_size = plic_addr_size;
|
||||
method Fabric_Addr m_plic_addr_lim = plic_addr_lim;
|
||||
|
||||
method Fabric_Addr m_uart0_addr_base = uart0_addr_base;
|
||||
method Fabric_Addr m_uart0_addr_size = uart0_addr_size;
|
||||
method Fabric_Addr m_uart0_addr_lim = uart0_addr_lim;
|
||||
|
||||
method Fabric_Addr m_boot_rom_addr_base = boot_rom_addr_base;
|
||||
method Fabric_Addr m_boot_rom_addr_size = boot_rom_addr_size;
|
||||
method Fabric_Addr m_boot_rom_addr_lim = boot_rom_addr_lim;
|
||||
|
||||
method Fabric_Addr m_mem0_controller_addr_base = mem0_controller_addr_base;
|
||||
method Fabric_Addr m_mem0_controller_addr_size = mem0_controller_addr_size;
|
||||
method Fabric_Addr m_mem0_controller_addr_lim = mem0_controller_addr_lim;
|
||||
|
||||
method Fabric_Addr m_tcm_addr_base = tcm_addr_base;
|
||||
method Fabric_Addr m_tcm_addr_size = tcm_addr_size;
|
||||
method Fabric_Addr m_tcm_addr_lim = tcm_addr_lim;
|
||||
|
||||
method Bool m_is_mem_addr (Fabric_Addr addr) = fn_is_mem_addr (addr);
|
||||
|
||||
method Bool m_is_IO_addr (Fabric_Addr addr) = fn_is_IO_addr (addr);
|
||||
|
||||
method Bool m_is_near_mem_IO_addr (Fabric_Addr addr) = fn_is_near_mem_io_addr (addr);
|
||||
|
||||
method Bit #(64) m_pc_reset_value = pc_reset_value;
|
||||
method Bit #(64) m_mtvec_reset_value = mtvec_reset_value;
|
||||
method Bit #(64) m_nmivec_reset_value = nmivec_reset_value;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
// Count and master-numbers of masters in the fabric.
|
||||
|
||||
typedef 2 Num_Masters;
|
||||
|
||||
Integer imem_master_num = 0;
|
||||
Integer dmem_master_num = 1;
|
||||
|
||||
// ================================================================
|
||||
// Count and slave-numbers of slaves in the fabric.
|
||||
|
||||
typedef 3 Num_Slaves;
|
||||
|
||||
Integer boot_rom_slave_num = 0;
|
||||
Integer mem0_controller_slave_num = 1;
|
||||
Integer uart0_slave_num = 2;
|
||||
|
||||
// ================================================================
|
||||
// Interrupt request numbers (== index in to vector of
|
||||
// interrupt-request lines in Core)
|
||||
|
||||
typedef 16 N_External_Interrupt_Sources;
|
||||
Integer n_external_interrupt_sources = valueOf (N_External_Interrupt_Sources);
|
||||
|
||||
Integer irq_num_uart0 = 0;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
354
src_Testbench/SoC/SoC_Top.bsv
Normal file
354
src_Testbench/SoC/SoC_Top.bsv
Normal file
@@ -0,0 +1,354 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package SoC_Top;
|
||||
|
||||
// ================================================================
|
||||
// This package is the SoC "top-level".
|
||||
|
||||
// (Note: there will be further layer(s) above this for
|
||||
// simulation top-level, FPGA top-level, etc.)
|
||||
|
||||
// ================================================================
|
||||
// Exports
|
||||
|
||||
export SoC_Top_IFC (..), mkSoC_Top;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import Memory :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// Main fabric
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
|
||||
import Fabric_Defs :: *;
|
||||
import SoC_Map :: *;
|
||||
import SoC_Fabric :: *;
|
||||
|
||||
// SoC components (CPU, mem, and IPs)
|
||||
|
||||
import Core_IFC :: *;
|
||||
import CoreW :: *;
|
||||
import PLIC :: *; // For interface to PLIC interrupt sources, in Core_IFC
|
||||
|
||||
import Boot_ROM :: *;
|
||||
import Mem_Controller :: *;
|
||||
import UART_Model :: *;
|
||||
|
||||
`ifdef INCLUDE_CAMERA_MODEL
|
||||
import Camera_Model :: *;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_ACCEL0
|
||||
import Accel_AES :: *;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
`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
|
||||
|
||||
interface SoC_Top_IFC;
|
||||
// Set core's verbosity
|
||||
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;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// To tandem verifier
|
||||
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
|
||||
`endif
|
||||
|
||||
// External real memory
|
||||
interface MemoryClient #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word) to_raw_mem;
|
||||
|
||||
// UART0 to external console
|
||||
interface Get #(Bit #(8)) get_to_console;
|
||||
interface Put #(Bit #(8)) put_from_console;
|
||||
|
||||
// For ISA tests: watch memory writes to <tohost> addr
|
||||
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// The module
|
||||
|
||||
(* synthesize *)
|
||||
module mkSoC_Top (SoC_Top_IFC);
|
||||
Integer verbosity = 0; // Normally 0; non-zero for debugging
|
||||
|
||||
Reg #(SoC_State) rg_state <- mkReg (SOC_START);
|
||||
|
||||
// SoC address map specifying base and limit for memories, IPs, etc.
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// Core: CPU + Near_Mem_IO (CLINT) + PLIC + Debug module (optional) + TV (optional)
|
||||
Core_IFC #(N_External_Interrupt_Sources) core <- mkCoreW;
|
||||
|
||||
// SoC Fabric
|
||||
Fabric_AXI4_IFC fabric <- mkFabric_AXI4;
|
||||
|
||||
// SoC Boot ROM
|
||||
Boot_ROM_IFC boot_rom <- mkBoot_ROM;
|
||||
|
||||
// SoC Memory
|
||||
Mem_Controller_IFC mem0_controller <- mkMem_Controller;
|
||||
|
||||
// SoC IPs
|
||||
UART_IFC uart0 <- mkUART;
|
||||
|
||||
`ifdef INCLUDE_ACCEL0
|
||||
// Accel0 master to fabric
|
||||
Accel_AES_IFC accel_aes0 <- mkAccel_AES;
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// SoC fabric master connections
|
||||
// Note: see 'SoC_Map' for 'master_num' definitions
|
||||
|
||||
// CPU IMem master to fabric
|
||||
mkConnection (core.cpu_imem_master, fabric.v_from_masters [imem_master_num]);
|
||||
|
||||
// CPU DMem master to fabric
|
||||
mkConnection (core.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]);
|
||||
`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 Mem Controller
|
||||
mkConnection (fabric.v_to_slaves [mem0_controller_slave_num], mem0_controller.slave);
|
||||
|
||||
// Fabric to UART0
|
||||
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);
|
||||
`endif
|
||||
|
||||
`ifdef HTIF_MEMORY
|
||||
AXI4_Slave_IFC#(Wd_Id, Wd_Addr, Wd_Data, Wd_User) htif <- mkAxi4LRegFile(bytes_per_htif);
|
||||
|
||||
mkConnection (fabric.v_to_slaves [htif_slave_num], htif);
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// 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
|
||||
core.core_external_interrupt_sources [irq_num_uart0].m_interrupt_req (intr);
|
||||
|
||||
// Tie off remaining interrupt request lines (1..N)
|
||||
for (Integer j = 1; j < valueOf (N_External_Interrupt_Sources); j = j + 1)
|
||||
core.core_external_interrupt_sources [j].m_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;
|
||||
*/
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// RESET BEHAVIOR WITHOUT DEBUG MODULE
|
||||
|
||||
rule rl_reset_start_2 (rg_state == SOC_START);
|
||||
core.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. Reset start ...", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR WITH DEBUG MODULE
|
||||
|
||||
`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;
|
||||
core.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 <- core.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;
|
||||
core.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 <- core.dm_ndm_reset_req_get.get;
|
||||
|
||||
core.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
|
||||
`endif
|
||||
|
||||
rule rl_reset_complete (rg_state == SOC_RESETTING);
|
||||
let cpu_rsp <- core.cpu_reset_server.response.get;
|
||||
let mem0_controller_rsp <- mem0_controller.server_reset.response.get;
|
||||
let uart0_rsp <- uart0.server_reset.response.get;
|
||||
|
||||
// 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);
|
||||
|
||||
mem0_controller.set_addr_map (soc_map.m_mem0_controller_addr_base,
|
||||
soc_map.m_mem0_controller_addr_lim);
|
||||
|
||||
uart0.set_addr_map (soc_map.m_uart0_addr_base, soc_map.m_uart0_addr_lim);
|
||||
|
||||
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
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
|
||||
core.set_verbosity (verbosity, logdelay);
|
||||
endmethod
|
||||
|
||||
// To external controller (E.g., GDB)
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
interface server_external_control = toGPServer (f_external_control_reqs, f_external_control_rsps);
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// To tandem verifier
|
||||
interface tv_verifier_info_get = core.tv_verifier_info_get;
|
||||
`endif
|
||||
|
||||
// External real memory
|
||||
interface to_raw_mem = mem0_controller.to_raw_mem;
|
||||
|
||||
// UART to external console
|
||||
interface get_to_console = uart0.get_to_console;
|
||||
interface put_from_console = uart0.put_from_console;
|
||||
|
||||
// For ISA tests: watch memory writes to <tohost> addr
|
||||
method Action set_watch_tohost (Bool watch_tohost, Fabric_Addr tohost_addr);
|
||||
mem0_controller.set_watch_tohost (watch_tohost, tohost_addr);
|
||||
endmethod
|
||||
endmodule: mkSoC_Top
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
371
src_Testbench/SoC/Timer.bsv
Normal file
371
src_Testbench/SoC/Timer.bsv
Normal file
@@ -0,0 +1,371 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Timer;
|
||||
|
||||
// ================================================================
|
||||
// This package implements a slave IP with two unrelated pieces of
|
||||
// RISC-V functionality:
|
||||
//
|
||||
// - real-time timer:
|
||||
// Two 64-bit memory-mapped registers (rg_time and rg_timecmp).
|
||||
// Delivers an external interrupt whenever rg_timecmp >= rg_time.
|
||||
// The timer is cleared when rg_timecmp is written.
|
||||
// Can be used for the RISC-V v1.10 Privilege Spec 'mtime' and
|
||||
// 'mtimecmp', and provides a memory-mapped API to access them.
|
||||
//
|
||||
// Offset/Size Name Function
|
||||
// 'h_4000/8 Bytes mtimecmp R/W the hart0 mtimecmp register
|
||||
// 'h_BFF8/8 Bytes mtime R/W the mtime register
|
||||
//
|
||||
// - Memory-mapped location for software interrupts.
|
||||
//
|
||||
// Offset/Size Name Function
|
||||
// 'h_0000/8 Bytes msip R/W Writing LSB=1 generates a software interrupt to hart0
|
||||
//
|
||||
// ----------------
|
||||
// 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.
|
||||
//
|
||||
// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
|
||||
// necessary to satisfy type-checking.
|
||||
// ================================================================
|
||||
|
||||
export Timer_IFC (..), mkTimer;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import ByteLane :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import Fabric_Defs :: *;
|
||||
import AXI4_Lite_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// Local constants and types
|
||||
|
||||
// Module state
|
||||
typedef enum {MODULE_STATE_START, MODULE_STATE_READY } Module_State
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface Timer_IFC;
|
||||
// Reset
|
||||
interface Server #(Bit #(0), Bit #(0)) server_reset;
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface AXI4_Lite_Slave_IFC #(Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
|
||||
// Timer interrupt
|
||||
// True/False = set/clear interrupt-pending in CPU's MTIP
|
||||
interface Get #(Bool) get_timer_interrupt_req;
|
||||
|
||||
// Software interrupt
|
||||
interface Get #(Bool) get_sw_interrupt_req;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkTimer (Timer_IFC);
|
||||
|
||||
// Verbosity: 0: quiet; 1: reset; 2: timer interrupts, all reads and writes
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
Reg #(Module_State) rg_state <- mkReg (MODULE_STATE_START);
|
||||
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
|
||||
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
|
||||
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Connector to fabric
|
||||
|
||||
AXI4_Lite_Slave_Xactor_IFC #(Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Lite_Slave_Xactor;
|
||||
|
||||
// ----------------
|
||||
// Timer registers
|
||||
|
||||
Reg #(Bit #(64)) crg_time [2] <- mkCReg (2, 1);
|
||||
Reg #(Bit #(64)) crg_timecmp [2] <- mkCReg (2, 0);
|
||||
|
||||
Reg #(Bool) rg_mtip <- mkReg (True);
|
||||
|
||||
// Timer interrupt queue
|
||||
FIFOF #(Bool) f_timer_interrupt_req <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Software-interrupt registers
|
||||
|
||||
Reg #(Bool) rg_msip <- mkRegU;
|
||||
|
||||
// Software interrupt queue
|
||||
FIFOF #(Bool) f_sw_interrupt_req <- mkFIFOF;
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Reset
|
||||
|
||||
// ns: 06/12/17 -- GDB reset bug fix
|
||||
// The explicit condition was preventing the Timer from being reset by GDB
|
||||
// after the initial hardware reset. This meant that on issuing a reset
|
||||
// command from GDB, control was never returned by hardware. The explicit
|
||||
// condition is not necessary as on a GDB reset, it's okay if the Timer
|
||||
// returns to its reset state irrespective of its current state
|
||||
// rule rl_reset (rg_state == MODULE_STATE_START);
|
||||
rule rl_reset;
|
||||
f_reset_reqs.deq;
|
||||
slave_xactor.reset;
|
||||
f_timer_interrupt_req.clear;
|
||||
f_sw_interrupt_req.clear;
|
||||
|
||||
rg_state <= MODULE_STATE_READY;
|
||||
crg_time [1] <= 1;
|
||||
crg_timecmp [1] <= 0;
|
||||
rg_mtip <= True;
|
||||
rg_msip <= False;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
|
||||
if (cfg_verbosity != 0)
|
||||
$display ("%0d: Timer.rl_reset", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Keep time and generate interrupt
|
||||
|
||||
// Increment time, but saturate, do not wrap-around
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule rl_tick_timer ((rg_state == MODULE_STATE_READY) && (crg_time [0] != '1));
|
||||
crg_time [0] <= crg_time [0] + 1;
|
||||
endrule
|
||||
|
||||
// Compare and generate timer interrupt request
|
||||
Bool new_mtip = (crg_time [0] >= crg_timecmp [0]);
|
||||
rule rl_compare ((rg_state == MODULE_STATE_READY)
|
||||
&& (rg_mtip != new_mtip));
|
||||
rg_mtip <= new_mtip;
|
||||
f_timer_interrupt_req.enq (new_mtip);
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Timer.rl_compare: new MTIP = %0d, time = %0d, timecmp = %0d",
|
||||
cur_cycle, new_mtip, crg_time [0], crg_timecmp [0]);
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric read requests
|
||||
|
||||
rule rl_process_rd_req (rg_state == MODULE_STATE_READY);
|
||||
let rda <- pop_o (slave_xactor.o_rd_addr);
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: Timer.rl_process_rd_req: rg_mtip = %0d", cur_cycle, rg_mtip);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
|
||||
let byte_addr = rda.araddr - rg_addr_base;
|
||||
|
||||
Bit #(Wd_Data) rdata = 0;
|
||||
AXI4_Lite_Resp rresp = AXI4_LITE_OKAY;
|
||||
|
||||
case (byte_addr)
|
||||
'h_0000: rdata = zeroExtend (rg_msip ? 1'b1 : 1'b0);
|
||||
'h_4000: rdata = truncate (crg_timecmp [0]); // truncates for 32b fabrics
|
||||
'h_BFF8: rdata = truncate (crg_time [0]); // truncates for 32b fabrics
|
||||
|
||||
// The following ALIGN4B reads are only needed for 32b fabrics
|
||||
'h_0004: rdata = 0;
|
||||
'h_4004: rdata = zeroExtend (crg_timecmp [0] [63:32]); // extends for 64b fabrics
|
||||
'h_BFFC: rdata = zeroExtend (crg_time [0] [63:32]); // extends for 64b fabrics
|
||||
default: begin
|
||||
rresp = AXI4_LITE_SLVERR;
|
||||
|
||||
$display ("%0d: ERROR: Timer.rl_process_rd_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
endcase
|
||||
|
||||
let rdr = AXI4_Lite_Rd_Data {rresp: rresp, rdata: rdata, ruser: rda.aruser};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" <= ", fshow (rdr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric write requests
|
||||
|
||||
rule rl_process_wr_req (rg_state == MODULE_STATE_READY);
|
||||
let wra <- pop_o (slave_xactor.o_wr_addr);
|
||||
let wrd <- pop_o (slave_xactor.o_wr_data);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: Timer.rl_process_wr_req: rg_mtip = %0d", cur_cycle, rg_mtip);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
end
|
||||
|
||||
let byte_addr = wra.awaddr - rg_addr_base;
|
||||
|
||||
AXI4_Lite_Resp bresp = AXI4_LITE_OKAY;
|
||||
|
||||
case (byte_addr)
|
||||
'h_0000: begin
|
||||
Bool new_msip = (wrd.wdata [0] == 1'b1);
|
||||
if (rg_msip != new_msip) begin
|
||||
rg_msip <= new_msip;
|
||||
f_sw_interrupt_req.enq (new_msip);
|
||||
if (cfg_verbosity > 1)
|
||||
$display (" new MSIP = %0d", new_msip);
|
||||
end
|
||||
end
|
||||
'h_4000: begin
|
||||
Bit #(64) old_timecmp = crg_timecmp [1];
|
||||
Bit #(64) new_timecmp = fn_update_strobed_bytes (old_timecmp,
|
||||
zeroExtend (wrd.wdata),
|
||||
zeroExtend (wrd.wstrb));
|
||||
crg_timecmp [1] <= new_timecmp;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" Writing MTIMECMP");
|
||||
$display (" old MTIMECMP = 0x%0h", old_timecmp);
|
||||
$display (" new MTIMECMP = 0x%0h", new_timecmp);
|
||||
$display (" cur MTIME = 0x%0h", crg_time [1]);
|
||||
$display (" new MTIMECMP - MTIME = 0x%0h", new_timecmp - crg_time [1]);
|
||||
end
|
||||
end
|
||||
'h_BFF8: begin
|
||||
Bit #(64) old_time = crg_time [1];
|
||||
Bit #(64) new_time = fn_update_strobed_bytes (old_time,
|
||||
zeroExtend (wrd.wdata),
|
||||
zeroExtend (wrd.wstrb));
|
||||
crg_time [1] <= new_time;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" Writing MTIME");
|
||||
$display (" old MTIME = 0x%0h", old_time);
|
||||
$display (" new MTIME = 0x%0h", new_time);
|
||||
end
|
||||
end
|
||||
|
||||
// The following ALIGN4B writes are only needed for 32b fabrics
|
||||
'h_0004: noAction;
|
||||
'h_4004: begin
|
||||
Bit #(64) old_timecmp = crg_timecmp [1];
|
||||
Bit #(64) new_timecmp = fn_update_strobed_bytes (old_timecmp,
|
||||
{ wrd.wdata [31:0], 32'h0 },
|
||||
{ wrd.wstrb [3:0], 4'h0 });
|
||||
crg_timecmp [1] <= new_timecmp;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" Writing MTIMECMP");
|
||||
$display (" old MTIMECMP = 0x%0h", old_timecmp);
|
||||
$display (" new MTIMECMP = 0x%0h", new_timecmp);
|
||||
$display (" cur MTIME = 0x%0h", crg_time [1]);
|
||||
$display (" new MTIMECMP - MTIME = 0x%0h", new_timecmp - crg_time [1]);
|
||||
end
|
||||
end
|
||||
'h_BFFC: begin
|
||||
Bit #(64) old_time = crg_time [1];
|
||||
Bit #(64) new_time = fn_update_strobed_bytes (old_time,
|
||||
{ wrd.wdata [31:0], 32'h0 },
|
||||
{ wrd.wstrb [3:0], 4'h0 });
|
||||
crg_time [1] <= new_time;
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" Writing MTIME");
|
||||
$display (" old MTIME = 0x%0h", old_time);
|
||||
$display (" new MTIME = 0x%0h", new_time);
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
$display ("%0d: ERROR: Timer.rl_process_wr_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
bresp = AXI4_LITE_SLVERR;
|
||||
end
|
||||
endcase
|
||||
|
||||
let wrr = AXI4_Lite_Wr_Resp {bresp: bresp, buser: wra.awuser};
|
||||
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" <= ", fshow (wrr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
if (addr_base [1:0] != 0)
|
||||
$display ("%0d: WARNING: Timer.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_base);
|
||||
|
||||
if (addr_lim [1:0] != 0)
|
||||
$display ("%0d: WARNING: Timer.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_lim);
|
||||
|
||||
rg_addr_base <= addr_base;
|
||||
rg_addr_lim <= addr_lim;
|
||||
endmethod
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface slave = slave_xactor.axi_side;
|
||||
|
||||
// External interrupt
|
||||
interface Get get_timer_interrupt_req;
|
||||
method ActionValue#(Bool) get();
|
||||
let x <- toGet (f_timer_interrupt_req).get;
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Timer: get_timer_interrupt_req: %x", cur_cycle, x);
|
||||
return x;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
// Software interrupt
|
||||
interface Get get_sw_interrupt_req;
|
||||
method ActionValue#(Bool) get();
|
||||
let x <- toGet (f_sw_interrupt_req).get;
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("%0d: Timer: get_sw_interrupt_req: %x", cur_cycle, x);
|
||||
return x;
|
||||
endmethod
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
451
src_Testbench/SoC/UART_Model.bsv
Normal file
451
src_Testbench/SoC/UART_Model.bsv
Normal file
@@ -0,0 +1,451 @@
|
||||
// Copyright (c) 2016-2019 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 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.
|
||||
//
|
||||
// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
|
||||
// necessary to satisfy type-checking.
|
||||
// ================================================================
|
||||
|
||||
export UART_IFC (..), mkUART;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// UART registers and their address offsets
|
||||
|
||||
Bit #(3) addr_UART_rbr = 3'h_0; // receiver buffer register (read only)
|
||||
Bit #(3) addr_UART_thr = 3'h_0; // transmitter holding register (write only)
|
||||
Bit #(3) addr_UART_ier = 3'h_1; // interrupt enable register
|
||||
Bit #(3) addr_UART_iir = 3'h_2; // interrupt id register (read-only)
|
||||
Bit #(3) addr_UART_lcr = 3'h_3; // line control reg
|
||||
Bit #(3) addr_UART_mcr = 3'h_4; // modem control reg
|
||||
Bit #(3) addr_UART_lsr = 3'h_5; // line status reg (read-only)
|
||||
Bit #(3) addr_UART_msr = 3'h_6; // modem status reg (read-only)
|
||||
Bit #(3) addr_UART_scr = 3'h_7; // scratch pad reg
|
||||
|
||||
// Aliased registers, depending on control bits
|
||||
Bit #(3) addr_UART_dll = 3'h_0; // divisor latch low
|
||||
Bit #(3) addr_UART_dlm = 3'h_1; // divisor latch high
|
||||
Bit #(3) addr_UART_fcr = 3'h_2; // fifo control reg (write-only)
|
||||
|
||||
// Bit fields of ier (Interrupt Enable Register)
|
||||
Bit #(8) uart_ier_erbfi = 8'h_01; // Enable Received Data Available Interrupt
|
||||
Bit #(8) uart_ier_etbei = 8'h_02; // Enable Transmitter Holding Register Empty Interrupt
|
||||
Bit #(8) uart_ier_elsi = 8'h_04; // Enable Receiver Line Status Interrupt
|
||||
Bit #(8) uart_ier_edssi = 8'h_08; // Enable Modem Status Interrupt
|
||||
|
||||
// iir values (Interrupt Identification Register) in decreasing priority of interrupts
|
||||
Bit #(8) uart_iir_none = 8'h_01; // None (no interrupts pending)
|
||||
Bit #(8) uart_iir_rls = 8'h_06; // Receiver Line Status
|
||||
Bit #(8) uart_iir_rda = 8'h_04; // Received Data Available
|
||||
Bit #(8) uart_iir_cti = 8'h_0C; // Character Timeout Indication
|
||||
Bit #(8) uart_iir_thre = 8'h_02; // Transmitter Holding Register Empty
|
||||
Bit #(8) uart_iir_ms = 8'h_00; // Modem Status
|
||||
|
||||
// Bit fields of LCR
|
||||
Bit #(8) uart_lcr_dlab = 8'h_80; // Divisor latch access bit
|
||||
Bit #(8) uart_lcr_bc = 8'h_40; // Break control
|
||||
Bit #(8) uart_lcr_sp = 8'h_20; // Stick parity
|
||||
Bit #(8) uart_lcr_eps = 8'h_10; // Even parity
|
||||
Bit #(8) uart_lcr_pen = 8'h_08; // Parity enable
|
||||
Bit #(8) uart_lcr_stb = 8'h_04; // # of stop bits (0=1b,1=2b)
|
||||
Bit #(8) uart_lcr_wls = 8'h_03; // word len (0:5b,1:6b,2:7b,3:8b)
|
||||
|
||||
// Bit fields of LSR
|
||||
Bit #(8) uart_lsr_rxfe = 8'h_80; // Receiver FIFO error
|
||||
Bit #(8) uart_lsr_temt = 8'h_40; // Transmitter empty
|
||||
Bit #(8) uart_lsr_thre = 8'h_20; // THR empty
|
||||
Bit #(8) uart_lsr_bi = 8'h_10; // Break interrupt
|
||||
Bit #(8) uart_lsr_fe = 8'h_08; // Framing Error
|
||||
Bit #(8) uart_lsr_pe = 8'h_04; // Parity Error
|
||||
Bit #(8) uart_lsr_oe = 8'h_02; // Overrun Error
|
||||
Bit #(8) uart_lsr_dr = 8'h_01; // Data Ready
|
||||
|
||||
Bit #(8) uart_lsr_reset_value = (uart_lsr_temt | uart_lsr_thre);
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface UART_IFC;
|
||||
// Reset
|
||||
interface Server #(Bit #(0), Bit #(0)) server_reset;
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
|
||||
// To external console
|
||||
interface Get #(Bit #(8)) get_to_console;
|
||||
interface Put #(Bit #(8)) put_from_console;
|
||||
|
||||
// Interrupt pending
|
||||
(* always_ready *)
|
||||
method Bool intr;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// Local types and constants
|
||||
|
||||
// Module state
|
||||
typedef enum {STATE_START,
|
||||
STATE_READY
|
||||
} Module_State
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// ----------------
|
||||
// Split a bus address into (offset in UART, lsbs)
|
||||
|
||||
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];
|
||||
|
||||
return tuple3 (msbs, offset, lsbs);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkUART (UART_IFC);
|
||||
|
||||
Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
Reg #(Module_State) rg_state <- mkReg (STATE_START);
|
||||
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
|
||||
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
|
||||
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Connector to 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
|
||||
|
||||
FIFOF #(Bit #(8)) f_from_console <- mkFIFOF;
|
||||
FIFOF #(Bit #(8)) f_to_console <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// These are the 16550 UART registers
|
||||
// See fn_addr_offset() above for meaning of 'addr offset'
|
||||
|
||||
Reg #(Bit #(8)) rg_rbr <- mkRegU; // addr offset 0
|
||||
Reg #(Bit #(8)) rg_thr <- mkRegU; // addr offset 0
|
||||
Reg #(Bit #(8)) rg_dll <- mkReg (0); // addr offset 0
|
||||
|
||||
Reg #(Bit #(8)) rg_ier <- mkReg (0); // addr offset 1
|
||||
Reg #(Bit #(8)) rg_dlm <- mkReg (0); // addr offset 1
|
||||
|
||||
// IIR is a virtual read-only register computed from other regs
|
||||
Reg #(Bit #(8)) rg_fcr <- mkReg (0); // addr offset 2
|
||||
|
||||
Reg #(Bit #(8)) rg_lcr <- mkReg (0); // addr offset 3
|
||||
Reg #(Bit #(8)) rg_mcr <- mkReg (0); // addr offset 4
|
||||
Reg #(Bit #(8)) rg_lsr <- mkReg (uart_lsr_reset_value); // addr offset 5
|
||||
Reg #(Bit #(8)) rg_msr <- mkReg (0); // addr offset 6
|
||||
Reg #(Bit #(8)) rg_scr <- mkReg (0); // addr offset 7
|
||||
|
||||
// ----------------
|
||||
// Virtual read-only register IIR
|
||||
|
||||
function Bit #(8) fn_iir ();
|
||||
Bit #(8) iir = 0;
|
||||
|
||||
if ( ((rg_ier & uart_ier_erbfi) != 0) // Rx interrupt enabled
|
||||
&& ((rg_lsr & uart_lsr_dr) != 0)) // data ready
|
||||
iir = uart_iir_rda;
|
||||
|
||||
else if ((rg_ier & uart_ier_etbei) != 0) // Tx Holding Reg Empty intr enabled
|
||||
iir = uart_iir_thre;
|
||||
|
||||
return iir;
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Test if an interrupt is pending
|
||||
|
||||
function Bool fn_intr ();
|
||||
let iir = fn_iir ();
|
||||
Bool eip = ((iir & uart_iir_none) == 0);
|
||||
return eip;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Soft reset (on token in f_reset_reqs)
|
||||
|
||||
rule rl_reset;
|
||||
f_reset_reqs.deq;
|
||||
|
||||
rg_dll <= 0;
|
||||
rg_ier <= 0;
|
||||
rg_dlm <= 0;
|
||||
rg_fcr <= 0;
|
||||
rg_lcr <= 0;
|
||||
rg_mcr <= 0;
|
||||
rg_lsr <= uart_lsr_reset_value;
|
||||
rg_msr <= 0;
|
||||
rg_scr <= 0;
|
||||
|
||||
slave_xactor.reset;
|
||||
rg_state <= STATE_READY;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
|
||||
if (cfg_verbosity != 0)
|
||||
$display ("%0d: UART.rl_reset", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric read requests
|
||||
|
||||
rule rl_process_rd_req (rg_state == STATE_READY);
|
||||
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));
|
||||
|
||||
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);
|
||||
$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);
|
||||
$display (" ", fshow (rda));
|
||||
rresp = axi4_resp_decerr;
|
||||
end
|
||||
|
||||
// offset 0: RBR
|
||||
else if ((offset == 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))
|
||||
rdata_byte = rg_dll;
|
||||
|
||||
// offset 1: IER
|
||||
else if ((offset == 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))
|
||||
rdata_byte = rg_dlm;
|
||||
|
||||
// offset 2: IIR (read-only)
|
||||
else if (offset == addr_UART_iir) rdata_byte = fn_iir();
|
||||
|
||||
// offset 3: LCR
|
||||
else if (offset == addr_UART_lcr) rdata_byte = { 0, rg_lcr };
|
||||
// offset 4: MCR
|
||||
else if (offset == addr_UART_mcr) rdata_byte = { 0, rg_mcr };
|
||||
// offset 5: LSR
|
||||
else if (offset == addr_UART_lsr) rdata_byte = { 0, rg_lsr };
|
||||
// offset 6: MSR
|
||||
else if (offset == addr_UART_msr) rdata_byte = { 0, rg_msr };
|
||||
// offset 7: SCR
|
||||
else if (offset == addr_UART_scr) rdata_byte = { 0, rg_scr };
|
||||
|
||||
else begin
|
||||
$display ("%0d: ERROR: UART.rl_process_rd_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
rresp = axi4_resp_decerr;
|
||||
end
|
||||
|
||||
// Send read-response to bus
|
||||
Fabric_Data rdata = zeroExtend (rdata_byte);
|
||||
let rdr = AXI4_Rd_Data {rid: rda.arid,
|
||||
rdata: rdata,
|
||||
rresp: rresp,
|
||||
rlast: True,
|
||||
ruser: rda.aruser};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: UART.rl_process_rd_req", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
$display (" ", fshow (rdr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle fabric write requests
|
||||
|
||||
rule rl_process_wr_req (rg_state == STATE_READY);
|
||||
let wra <- pop_o (slave_xactor.o_wr_addr);
|
||||
let wrd <- pop_o (slave_xactor.o_wr_data);
|
||||
|
||||
Bit #(64) wdata = zeroExtend (wrd.wdata);
|
||||
Bit #(8) wstrb = zeroExtend (wrd.wstrb);
|
||||
Bit #(8) data_byte = wdata [7:0];
|
||||
|
||||
let byte_addr = wra.awaddr - rg_addr_base;
|
||||
let { msbs, 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);
|
||||
$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);
|
||||
$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
|
||||
// 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))
|
||||
rg_dll <= data_byte;
|
||||
|
||||
// offset 1: IER
|
||||
else if ((offset == 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))
|
||||
rg_dlm <= data_byte;
|
||||
|
||||
// offset 2: FCR (write-only)
|
||||
else if (offset == addr_UART_fcr) rg_fcr <= data_byte;
|
||||
|
||||
// offset 3: LCR
|
||||
else if (offset == addr_UART_lcr) rg_lcr <= data_byte;
|
||||
// offset 4: MCR
|
||||
else if (offset == addr_UART_mcr) rg_mcr <= data_byte;
|
||||
// offset 5: LSR
|
||||
else if (offset == addr_UART_lsr) noAction; // LSR is read-only
|
||||
// offset 6: MSR
|
||||
else if (offset == addr_UART_msr) noAction; // MSR is read-only
|
||||
// offset 7: SCR
|
||||
else if (offset == addr_UART_scr) rg_scr <= data_byte;
|
||||
|
||||
else begin
|
||||
$display ("%0d: ERROR: UART.rl_process_wr_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
bresp = axi4_resp_decerr;
|
||||
end
|
||||
|
||||
// Send write-response to bus
|
||||
let wrr = AXI4_Wr_Resp {bid: wra.awid,
|
||||
bresp: bresp,
|
||||
buser: wra.awuser};
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: UART.rl_process_wr_req", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
$display (" ", fshow (wrr));
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Receive a char from the serial line when RBR is empty (i.e., LSR.DR is 0),
|
||||
// and deposit it into RBR
|
||||
// and set it full (LSR.DR = 1)
|
||||
|
||||
rule rl_receive ((rg_lsr & uart_lsr_dr) == 0);
|
||||
let ch <- pop (f_from_console);
|
||||
rg_rbr <= ch;
|
||||
|
||||
let new_lsr = (rg_lsr | uart_lsr_dr); // Set data-ready
|
||||
rg_lsr <= new_lsr;
|
||||
|
||||
if (cfg_verbosity > 1)
|
||||
$display ("UART_Model.rl_receive: received char 0x%0h; new_lsr = 0x%0h",
|
||||
ch, new_lsr);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
||||
if (addr_base [2:0] != 0)
|
||||
$display ("%0d: WARNING: UART.set_addr_map: addr_base 0x%0h is not 8-Byte-aligned",
|
||||
cur_cycle, addr_base);
|
||||
|
||||
if (addr_lim [2:0] != 0)
|
||||
$display ("%0d: WARNING: UART.set_addr_map: addr_lim 0x%0h is not 8-Byte-aligned",
|
||||
cur_cycle, addr_lim);
|
||||
|
||||
rg_addr_base <= addr_base;
|
||||
rg_addr_lim <= addr_lim;
|
||||
endmethod
|
||||
|
||||
// Main Fabric Reqs/Rsps
|
||||
interface slave = slave_xactor.axi_side;
|
||||
|
||||
// To external console
|
||||
interface put_from_console = toPut (f_from_console);
|
||||
interface get_to_console = toGet (f_to_console);
|
||||
|
||||
// Interrupt pending
|
||||
method Bool intr;
|
||||
return fn_intr ();
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
1050
src_Testbench/SoC/fn_read_ROM_RV32.bsvi
Normal file
1050
src_Testbench/SoC/fn_read_ROM_RV32.bsvi
Normal file
File diff suppressed because it is too large
Load Diff
1050
src_Testbench/SoC/fn_read_ROM_RV64.bsvi
Normal file
1050
src_Testbench/SoC/fn_read_ROM_RV64.bsvi
Normal file
File diff suppressed because it is too large
Load Diff
825
src_Testbench/Top/C_Imported_Functions.c
Normal file
825
src_Testbench/Top/C_Imported_Functions.c
Normal file
@@ -0,0 +1,825 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
// ================================================================
|
||||
// These are functions imported into BSV during Bluesim or Verilog simulation.
|
||||
// See C_Imports.bsv for the corresponding 'import BDPI' declarations.
|
||||
|
||||
// There are several independent groups of functions below; the
|
||||
// groups are separated by heavy dividers ('// *******')
|
||||
|
||||
// Below, 'dummy' args are not used, and are present only to appease
|
||||
// some Verilog simulators that are finicky about 0-arg functions.
|
||||
|
||||
// ================================================================
|
||||
// Includes from C library
|
||||
|
||||
// General
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
// For comms polling
|
||||
#include <sys/types.h>
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
|
||||
// For TCP
|
||||
#include <sys/socket.h> // socket definitions
|
||||
#include <sys/types.h> // socket types
|
||||
#include <arpa/inet.h> // inet (3) funtions
|
||||
#include <fcntl.h> // To set non-blocking mode
|
||||
|
||||
// ================================================================
|
||||
// Includes for this project
|
||||
|
||||
#include "C_Imported_Functions.h"
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for console I/O
|
||||
|
||||
// ================================================================
|
||||
// c_trygetchar()
|
||||
// Returns next input character (ASCII code) from the console.
|
||||
// Returns 0 if no input is available.
|
||||
|
||||
uint8_t c_trygetchar (uint8_t dummy)
|
||||
{
|
||||
uint8_t ch;
|
||||
ssize_t n;
|
||||
struct pollfd x_pollfd;
|
||||
const int fd_stdin = 0;
|
||||
|
||||
// ----------------
|
||||
// Poll for input
|
||||
x_pollfd.fd = fd_stdin;
|
||||
x_pollfd.events = POLLRDNORM;
|
||||
x_pollfd.revents = 0;
|
||||
poll (& x_pollfd, 1, 1);
|
||||
|
||||
// printf ("INFO: c_trygetchar: Polling for input\n");
|
||||
if ((x_pollfd.revents & POLLRDNORM) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
// Input is available
|
||||
|
||||
n = read (fd_stdin, & ch, 1);
|
||||
if (n == 1) {
|
||||
return ch;
|
||||
}
|
||||
else {
|
||||
if (n == 0)
|
||||
printf ("c_trygetchar: end of file\n");
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// A small 'main' to test c_trygetchar()
|
||||
|
||||
#ifdef TEST_TRYGETCHAR
|
||||
|
||||
char message[] = "Hello World!\n";
|
||||
|
||||
int main (int argc, char *argv [])
|
||||
{
|
||||
uint8_t ch;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < strlen (message); j++)
|
||||
c_putchar (message[j]);
|
||||
|
||||
printf ("Polling for input\n");
|
||||
|
||||
j = 0;
|
||||
while (1) {
|
||||
ch = c_trygetchar ();
|
||||
if (ch == 0xFF) break;
|
||||
if (ch != 0)
|
||||
printf ("Received character %0d 0x%0x '%c'\n", ch, ch, ch);
|
||||
else {
|
||||
printf ("\r%0d ", j);
|
||||
fflush (stdout);
|
||||
j++;
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ================================================================
|
||||
// c_putchar()
|
||||
// Writes character to stdout
|
||||
|
||||
uint32_t c_putchar (uint8_t ch)
|
||||
{
|
||||
int status;
|
||||
uint32_t success = 0;
|
||||
|
||||
if ((ch == 0) || (ch > 0x7F)) {
|
||||
// Discard non-printables
|
||||
success = 1;
|
||||
}
|
||||
else {
|
||||
if ((ch == '\n') || (' ' <= ch)) {
|
||||
status = fprintf (stdout, "%c", ch);
|
||||
if (status > 0)
|
||||
success = 1;
|
||||
}
|
||||
else {
|
||||
status = fprintf (stdout, "[\\%0d]", ch);
|
||||
if (status > 0)
|
||||
success = 1;
|
||||
}
|
||||
|
||||
if (success == 1) {
|
||||
status = fflush (stdout);
|
||||
if (status != 0)
|
||||
success = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for reading in various parameters
|
||||
|
||||
// ================================================================
|
||||
// c_get_symbol_val ()
|
||||
// Returns the value of a symbol (a memory address) from a symbol-table file.
|
||||
// The symbol-table file has a '<symbol> <value-in-hex>' pair on each line.
|
||||
// Reads the whole symbol-table file on each call,
|
||||
// which is ok if it's not called often and the file is small.
|
||||
|
||||
static
|
||||
char symbol_table_filename [] = "symbol_table.txt";
|
||||
|
||||
uint64_t c_get_symbol_val (char * symbol)
|
||||
{
|
||||
bool ok = false;
|
||||
uint64_t val = 0;
|
||||
|
||||
FILE *fp = fopen (symbol_table_filename, "r");
|
||||
if (fp == NULL) {
|
||||
fprintf (stderr, "c_get_symbol: could not open file: %s\n", symbol_table_filename);
|
||||
}
|
||||
else {
|
||||
int len = strlen (symbol);
|
||||
int linenum = 0;
|
||||
|
||||
while (true) {
|
||||
char linebuf [1024], *p;
|
||||
p = fgets (linebuf, 1024, fp);
|
||||
if (p == NULL) { // EOF
|
||||
fprintf (stderr, "c_get_symbol: could not find value for symbol %s\n", symbol);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
linenum++;
|
||||
|
||||
int cmp = strncmp (symbol, linebuf, len);
|
||||
if (cmp != 0) // This symbol is not on this line
|
||||
continue;
|
||||
else {
|
||||
int n = sscanf (& (linebuf [len]), "%" SCNx64, & val);
|
||||
if ((n == EOF) || (n < 1)) {
|
||||
fprintf (stderr, "c_get_symbol: could not find value for symbol %s\n", symbol);
|
||||
fprintf (stderr, " on file '%s' line %d\n", symbol_table_filename, linenum);
|
||||
}
|
||||
else
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// A small 'main' to test c_get_symbol_val()
|
||||
|
||||
#ifdef TEST_GET_SYMBOL_VAL
|
||||
|
||||
int main (int argc, char *argv [])
|
||||
{
|
||||
uint64_t x = c_get_symbol_val ("tohost");
|
||||
fprintf (stdout, "tohost value is 0x%" PRIx64 "\n", x);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ================================================================
|
||||
// Misc. parameters from a parameters file, if it exists.
|
||||
// Each non-empty line of the file should contain
|
||||
// param-name param-value comment
|
||||
// or # comment
|
||||
|
||||
/*
|
||||
#define MAX_PARAMS 128
|
||||
|
||||
char sim_params_filename[] = "sim_params.txt";
|
||||
static bool params_loaded = false;
|
||||
static uint64_t param_vals [MAX_PARAMS];
|
||||
|
||||
#define PARAM_LINEBUF_SIZE 1024
|
||||
|
||||
static
|
||||
void load_params (void)
|
||||
{
|
||||
for (uint32_t j = 0; j < MAX_PARAMS; j++)
|
||||
param_vals [j] = 0;
|
||||
|
||||
FILE *fp = fopen ("sim_params.txt", "r");
|
||||
if (fp == NULL) {
|
||||
fprintf (stdout, "INFO: no simulation parameters file: %s\n", sim_params_filename);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
fprintf (stdout, "INFO: loading simulation parameters from file: %s\n", sim_params_filename);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char linebuf [PARAM_LINEBUF_SIZE], *p;
|
||||
|
||||
p = fgets (linebuf, PARAM_LINEBUF_SIZE, fp);
|
||||
if (p == NULL) // EOF
|
||||
break;
|
||||
|
||||
// Skip leading blanks and tabs
|
||||
int index = 0;
|
||||
while ((index < PARAM_LINEBUF_SIZE)
|
||||
&& ((linebuf [index] == ' ') || (linebuf [index] == '\t')))
|
||||
index++;
|
||||
if (index == PARAM_LINEBUF_SIZE)
|
||||
break;
|
||||
|
||||
if (linebuf [index] == '#') // comment line
|
||||
continue;
|
||||
|
||||
// Read the parameter code
|
||||
uint32_t param;
|
||||
int n = sscanf (& linebuf [index], "%" SCNd32 "%n", & param, & index);
|
||||
if ((n == EOF) || (n < 1))
|
||||
continue;
|
||||
|
||||
// Read the parameter value (decimal or hex, depending on the parameter)
|
||||
uint64_t param_val;
|
||||
if (param == sim_param_watch_tohost) {
|
||||
int n = sscanf (& (linebuf [index]), "%" SCNd64, & param_val);
|
||||
}
|
||||
else if (param == sim_param_tohost_addr) {
|
||||
int n = sscanf (& (linebuf [index]), "%" SCNx64, & param_val);
|
||||
}
|
||||
else {
|
||||
int n = sscanf (& (linebuf [index]), "%" SCNd64, & param_val);
|
||||
}
|
||||
|
||||
if ((n == EOF) || (n < 1))
|
||||
continue;
|
||||
else if (param >= MAX_PARAMS) {
|
||||
fprintf (stderr, "INFO: ignoring out-of-bounds parameter code %0" PRId32 " (should be < %0d)\n",
|
||||
param, MAX_PARAMS);
|
||||
fprintf (stderr, " Value (decimal) = %0" PRId64, param_val);
|
||||
fprintf (stderr, " Value (hex) = 0x%0" PRIx64, param_val);
|
||||
}
|
||||
else {
|
||||
param_vals [param] = param_val;
|
||||
}
|
||||
}
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
uint64_t c_get_param_val (uint32_t param)
|
||||
{
|
||||
if (! params_loaded) {
|
||||
load_params ();
|
||||
params_loaded = true;
|
||||
}
|
||||
|
||||
uint64_t param_val = 0;
|
||||
if (param < MAX_PARAMS)
|
||||
param_val = param_vals [param];
|
||||
return param_val;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for Tandem Verification trace file output.
|
||||
|
||||
static char trace_file_name[] = "trace_data.dat";
|
||||
|
||||
static FILE *trace_file_stream;
|
||||
|
||||
static uint64_t trace_file_size = 0;
|
||||
static uint64_t trace_file_writes = 0;
|
||||
|
||||
#define BUFSIZE 1024
|
||||
static uint8_t buf [BUFSIZE];
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_open()
|
||||
// Open file for recording binary trace output.
|
||||
|
||||
uint32_t c_trace_file_open (uint8_t dummy)
|
||||
{
|
||||
uint32_t success = 0;
|
||||
|
||||
trace_file_stream = fopen ("trace_out.dat", "w");
|
||||
if (trace_file_stream == NULL) {
|
||||
fprintf (stderr, "ERROR: c_trace_file_open: unable to open file '%s'.\n", trace_file_name);
|
||||
success = 0;
|
||||
}
|
||||
else {
|
||||
fprintf (stdout, "c_trace_file_stream: opened file '%s' for trace_data.\n", trace_file_name);
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_byte_in_buffer ()
|
||||
// Write 8-bit 'data' into output buffer at byte offset 'j'
|
||||
|
||||
uint32_t c_trace_file_load_byte_in_buffer (uint32_t j, uint8_t data)
|
||||
{
|
||||
uint32_t success = 0;
|
||||
|
||||
if (j >= BUFSIZE) {
|
||||
fprintf (stderr, "ERROR: c_trace_file_load_byte_in_buffer: index (%0d) out of bounds (%0d)\n",
|
||||
j, BUFSIZE);
|
||||
success = 0;
|
||||
}
|
||||
else {
|
||||
buf [j] = data;
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_word64_in_buffer ()
|
||||
// Write 64-bit 'data' into output buffer at 'byte_offset'
|
||||
|
||||
uint32_t c_trace_file_load_word64_in_buffer (uint32_t byte_offset, uint64_t data)
|
||||
{
|
||||
uint32_t success = 0;
|
||||
|
||||
if ((byte_offset + 7) >= BUFSIZE) {
|
||||
fprintf (stderr, "ERROR: c_trace_file_load_word64_in_buffer: index (%0d) out of bounds (%0d)\n",
|
||||
byte_offset, BUFSIZE);
|
||||
success = 0;
|
||||
}
|
||||
else {
|
||||
uint64_t *p = (uint64_t *) & (buf [byte_offset]);
|
||||
*p = data;
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_write_buffer()
|
||||
// Write out 'n' bytes from the already-loaded output buffer to the trace file.
|
||||
|
||||
uint32_t c_trace_file_write_buffer (uint32_t n)
|
||||
{
|
||||
uint32_t success = 0;
|
||||
|
||||
size_t n_written = fwrite (buf, 1, n, trace_file_stream);
|
||||
if (n_written != n)
|
||||
success = 0;
|
||||
else {
|
||||
trace_file_size += n;
|
||||
trace_file_writes += 1;
|
||||
success = 1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_close()
|
||||
// Close the trace file.
|
||||
|
||||
uint32_t c_trace_file_close (uint8_t dummy)
|
||||
{
|
||||
uint32_t success = 0;
|
||||
int status;
|
||||
|
||||
if (trace_file_stream == NULL)
|
||||
success = 1;
|
||||
else {
|
||||
status = fclose (trace_file_stream);
|
||||
if (status != 0) {
|
||||
fprintf (stderr, "ERROR: c_trace_file_close: error in fclose()\n");
|
||||
success = 0;
|
||||
}
|
||||
else {
|
||||
fprintf (stdout, "c_trace_file_stream: closed file '%s' for trace_data.\n", trace_file_name);
|
||||
fprintf (stdout, " Trace file writes: %0" PRId64 "\n", trace_file_writes);
|
||||
fprintf (stdout, " Trace file size: %0" PRId64 " bytes\n", trace_file_size);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for communication with remote debug client.
|
||||
|
||||
// Acknowledgement: portions of TCP code adapted from example ECHOSERV
|
||||
// ECHOSERV
|
||||
// (c) Paul Griffiths, 1999
|
||||
// http://www.paulgriffiths.net/program/c/echoserv.php
|
||||
|
||||
// ================================================================
|
||||
// The socket file descriptor
|
||||
|
||||
static uint16_t port = 30000;
|
||||
|
||||
static int connected_sockfd = 0;
|
||||
|
||||
static FILE *logfile_fp = NULL;
|
||||
|
||||
static char logfile_name [] = "debug_server_log.txt";
|
||||
|
||||
// ================================================================
|
||||
// Connect to debug client as server on tcp_port.
|
||||
// Return fail/ok.
|
||||
|
||||
uint8_t c_debug_client_connect (const uint16_t tcp_port)
|
||||
{
|
||||
int listen_sockfd; // listening socket
|
||||
struct sockaddr_in servaddr; // socket address structure
|
||||
struct linger linger;
|
||||
|
||||
fprintf (stdout, "Awaiting remote debug client connection on tcp port %0d ...\n", tcp_port);
|
||||
|
||||
// Create the listening socket
|
||||
if ( (listen_sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0 ) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: socket () failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
// Set linger to 0 (immediate exit on close)
|
||||
linger.l_onoff = 1;
|
||||
linger.l_linger = 0;
|
||||
setsockopt (listen_sockfd, SOL_SOCKET, SO_LINGER, & linger, sizeof (linger));
|
||||
|
||||
// Initialize socket address structure
|
||||
memset (& servaddr, 0, sizeof (servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
|
||||
servaddr.sin_port = htons (tcp_port);
|
||||
|
||||
// Bind socket addresss to listening socket
|
||||
if ( bind (listen_sockfd, (struct sockaddr *) & servaddr, sizeof (servaddr)) < 0 ) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: bind () failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
// Listen for connection
|
||||
if ( listen (listen_sockfd, 1) < 0 ) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: listen () failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
// Set listening socket to non-blocking
|
||||
int flags = fcntl (listen_sockfd, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: fcntl (F_GETFL) failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
flags = (flags |O_NONBLOCK);
|
||||
if (fcntl (listen_sockfd, F_SETFL, flags) < 0) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: fcntl (F_SETFL, O_NONBLOCK) failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
// Wait for a connection, accept() it
|
||||
while (true) {
|
||||
connected_sockfd = accept (listen_sockfd, NULL, NULL);
|
||||
if ((connected_sockfd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
|
||||
sleep (1);
|
||||
}
|
||||
else if (connected_sockfd < 0) {
|
||||
fprintf (stderr, "ERROR: c_debug_client_connect: accept () failed\n");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Close the listening socket
|
||||
if (close (listen_sockfd) < 0) {
|
||||
perror ("ERROR: c_debug_client_connect: error in close (listen_sockfd)");
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
fprintf (stdout, "Connected\n");
|
||||
|
||||
logfile_fp = fopen (logfile_name, "w");
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (stdout, " Logfile for debug client transactions is '%s'\n", logfile_name);
|
||||
fprintf (logfile_fp, "CONNECTED on TCP port %0d\n", tcp_port);
|
||||
}
|
||||
else
|
||||
fprintf (stdout, " Unable to open logfile for debug client transactions: '%s'\n",
|
||||
logfile_name);
|
||||
|
||||
return DMI_STATUS_OK;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Disconnect from debug client as server.
|
||||
// Return fail/ok.
|
||||
|
||||
uint8_t c_debug_client_disconnect (uint8_t dummy)
|
||||
{
|
||||
uint8_t buf [128];
|
||||
ssize_t n;
|
||||
|
||||
fprintf (stdout, "Disconnected from remote debug client on port %0d\n", port);
|
||||
|
||||
shutdown (connected_sockfd, SHUT_WR);
|
||||
|
||||
// Drain remaining bytes arriving
|
||||
while (1) {
|
||||
n = recv (connected_sockfd, buf, 128, 0);
|
||||
if (n == 0)
|
||||
break;
|
||||
if ((n == -1) && (errno != EINTR))
|
||||
break;
|
||||
}
|
||||
|
||||
if (close (connected_sockfd) < 0) {
|
||||
perror ("ERROR: c_debug_client_disconnect:");
|
||||
fprintf (stderr, " socket file descriptor: %0d\n", connected_sockfd);
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "DISCONNECTED on port %0d\n", port);
|
||||
fclose (logfile_fp);
|
||||
}
|
||||
|
||||
return DMI_STATUS_OK;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Receive 7-byte request from remote client
|
||||
// Result is: { status, data_b3, data_b2, data_b1, data_b0, addr_b1, addr_b0, op }
|
||||
|
||||
static int command_num = 0;
|
||||
|
||||
uint64_t c_debug_client_request_recv (uint8_t dummy)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
uint8_t *p_result = (uint8_t *) & result;
|
||||
|
||||
// ----------------
|
||||
// First, poll to check if any data is available
|
||||
int fd = connected_sockfd;
|
||||
|
||||
struct pollfd x_pollfd;
|
||||
x_pollfd.fd = fd;
|
||||
x_pollfd.events = POLLRDNORM;
|
||||
x_pollfd.revents = 0;
|
||||
|
||||
int n = poll (& x_pollfd, 1, 0);
|
||||
|
||||
if (n < 0) {
|
||||
perror ("ERROR: c_debug_client_request_recv (): poll () failed");
|
||||
p_result [7] = DMI_STATUS_ERR;
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((x_pollfd.revents & POLLRDNORM) == 0) {
|
||||
// No byte available
|
||||
// sched_yield (); // Allow other threads to run.
|
||||
p_result [7] = DMI_STATUS_UNAVAIL;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
// Data is available; read the 7-byte request
|
||||
|
||||
int data_size = 7;
|
||||
int n_recd = 0;
|
||||
int n_iters = 0;
|
||||
p_result [0] = DMI_STATUS_ERR;
|
||||
while (n_recd < data_size) {
|
||||
int n = read (fd, p_result + n_recd, (data_size - n_recd));
|
||||
if ((n < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "ERROR: c_debug_client_request_recv () failed\n");
|
||||
fprintf (logfile_fp, " Received %0d bytes so far (of %0d)\n", n_recd, data_size);
|
||||
fprintf (logfile_fp, " Data so far: 0x%0" PRIx64 "\n", result);
|
||||
fprintf (logfile_fp, " read (sock, ...) => %0d\n", n);
|
||||
}
|
||||
p_result [7] = DMI_STATUS_ERR;
|
||||
return result;
|
||||
}
|
||||
else if (n > 0) {
|
||||
n_recd += n;
|
||||
}
|
||||
|
||||
n_iters++;
|
||||
if ((n_iters > 0) && ((n_iters % 1000000) == 0)) {
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "WARNING: c_debug_client_request_recv () stalled?\n");
|
||||
fprintf (logfile_fp, " Received %0d bytes so far (of %0d)\n", n_recd, data_size);
|
||||
fprintf (logfile_fp, " Data so far: 0x%0" PRIx64 "\n", result);
|
||||
fprintf (logfile_fp, " %0d iterations so far\n", n_iters);
|
||||
}
|
||||
}
|
||||
}
|
||||
p_result [7] = DMI_STATUS_OK;
|
||||
|
||||
if (logfile_fp != NULL) {
|
||||
uint8_t op = (result & 0xFF);
|
||||
uint16_t addr = ((result >> 8) & 0xFFFF);
|
||||
uint32_t data = ((result >> 24) & 0xFFFFFFFF);
|
||||
switch (op) {
|
||||
|
||||
case DMI_OP_READ: {
|
||||
fprintf (logfile_fp, "C_to_S READ 0x%04x\n", addr);
|
||||
break;
|
||||
}
|
||||
case DMI_OP_WRITE: {
|
||||
fprintf (logfile_fp, "C_to_S WRITE 0x%04x 0x%08x\n", addr, data);
|
||||
break;
|
||||
}
|
||||
case DMI_OP_SHUTDOWN: {
|
||||
fprintf (logfile_fp, "C_to_S SHUTDOWN\n");
|
||||
break;
|
||||
}
|
||||
case DMI_OP_START_COMMAND: {
|
||||
fprintf (logfile_fp, "C_to_S ======== START_COMMAND %0d\n", command_num);
|
||||
command_num++;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf (logfile_fp, "C_to_S ERROR: Unrecognized op %0d; ignored\n", op);
|
||||
|
||||
fprintf (stderr,
|
||||
"ERROR: c_debug_client_request_recv: Unrecognized op %0d; ignored\n",
|
||||
op);
|
||||
}
|
||||
}
|
||||
fflush (logfile_fp);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Send 4-byte response 'data' to debug client.
|
||||
// Returns fail/ok status
|
||||
|
||||
uint8_t c_debug_client_response_send (const uint32_t data)
|
||||
{
|
||||
int fd = connected_sockfd;
|
||||
int data_size = 4; // 4 bytes
|
||||
int n_sent = 0;
|
||||
int n_iters = 0;
|
||||
uint8_t *p_data = (uint8_t *) & data;
|
||||
|
||||
while (n_sent < data_size) {
|
||||
int n = write (fd, p_data + n_sent, (data_size - n_sent));
|
||||
if ((n < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "ERROR: c_debug_client_response_send (0x%08x) failed\n", data);
|
||||
fprintf (logfile_fp, " Sent %0d bytes so far (of %0d)\n", n_sent, data_size);
|
||||
fprintf (logfile_fp, " write (sock, ...) => %0d\n", n);
|
||||
}
|
||||
return DMI_STATUS_ERR;
|
||||
}
|
||||
else if (n > 0) {
|
||||
n_sent += n;
|
||||
}
|
||||
|
||||
n_iters++;
|
||||
if ((n_iters > 0) && ((n_iters % 0x1000000) == 0)) {
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "WARNING: c_debug_client_response_send (0x%08x) stalled?\n", data);
|
||||
fprintf (logfile_fp, " Sent %0d bytes so far (of %0d)\n", n_sent, data_size);
|
||||
fprintf (logfile_fp, " %0d iterations so far\n", n_iters);
|
||||
}
|
||||
}
|
||||
}
|
||||
fsync (fd);
|
||||
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "S_to_C 0x%08x\n", data);
|
||||
fflush (logfile_fp);
|
||||
}
|
||||
|
||||
return DMI_STATUS_OK;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// This 'main' procedure is for standalone testing of this C server code.
|
||||
// It listens on the server socket for a connection from Dsharp.
|
||||
// It simply prints out read/write commands from Dsharp,
|
||||
// and responds to read commands with an incrementing integer.
|
||||
|
||||
#ifdef TEST_COMMS
|
||||
|
||||
int main (int argc, char *argv [])
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
uint64_t req;
|
||||
uint8_t *p_req = (uint8_t *) (& req);
|
||||
uint8_t req_op;
|
||||
uint16_t req_addr;
|
||||
uint32_t req_data;
|
||||
|
||||
uint32_t rsp_data = 0;
|
||||
|
||||
status = c_debug_client_connect (port);
|
||||
if (status != DMI_STATUS_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
req = c_debug_client_request_recv (0);
|
||||
|
||||
status = p_req [7];
|
||||
|
||||
req_op = p_req [0];
|
||||
|
||||
req_addr = p_req [2];
|
||||
req_addr = ((req_addr << 8) | p_req [1]);
|
||||
|
||||
req_data = p_req [6];
|
||||
req_data = ((req_data << 8) | p_req [5]);
|
||||
req_data = ((req_data << 8) | p_req [4]);
|
||||
req_data = ((req_data << 8) | p_req [3]);
|
||||
|
||||
if (status == DMI_STATUS_UNAVAIL) {
|
||||
usleep (1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == DMI_STATUS_ERR)
|
||||
break;
|
||||
|
||||
if (logfile_fp != NULL)
|
||||
fprintf (logfile_fp, "================\n");
|
||||
if (req_op == DMI_OP_SHUTDOWN) {
|
||||
if (logfile_fp != NULL)
|
||||
fprintf (logfile_fp, "SHUTDOWN\n");
|
||||
break;
|
||||
}
|
||||
else if (req_op == DMI_OP_READ) {
|
||||
if (logfile_fp != NULL) {
|
||||
fprintf (logfile_fp, "READ addr '%04x'\n", req_addr);
|
||||
fprintf (logfile_fp, " => sending response 0x%08x\n", rsp_data);
|
||||
}
|
||||
status = c_debug_client_response_send (rsp_data);
|
||||
if (status == DMI_STATUS_ERR)
|
||||
break;
|
||||
rsp_data++;
|
||||
}
|
||||
else if (req_op == DMI_OP_WRITE) {
|
||||
if (logfile_fp != NULL)
|
||||
fprintf (logfile_fp, "WRITE addr '%04x' data '%08x'\n", req_addr, req_data);
|
||||
}
|
||||
else {
|
||||
if (logfile_fp != NULL)
|
||||
fprintf (stderr, "ERROR: unknown command: %0d\n", req_op);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
134
src_Testbench/Top/C_Imported_Functions.h
Normal file
134
src_Testbench/Top/C_Imported_Functions.h
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
#pragma once
|
||||
|
||||
// ================================================================
|
||||
// These are functions imported into BSV during Bluesim or Verilog simulation.
|
||||
// See C_Imports.bsv for the corresponding 'import BDPI' declarations.
|
||||
|
||||
// There are several independent groups of functions below; the
|
||||
// groups are separated by heavy dividers ('// *******')
|
||||
|
||||
// Below, 'dummy' args are not used, and are present only to appease
|
||||
// some Verilog simulators that are finicky about 0-arg functions.
|
||||
|
||||
// ================================================================
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for console I/O
|
||||
|
||||
// ================================================================
|
||||
// c_trygetchar()
|
||||
// Returns next input character (ASCII code) from the console.
|
||||
// Returns 0 if no input is available.
|
||||
|
||||
extern
|
||||
uint8_t c_trygetchar (uint8_t dummy);
|
||||
|
||||
// ================================================================
|
||||
// c_putchar()
|
||||
// Writes character to stdout
|
||||
|
||||
extern
|
||||
uint32_t c_putchar (uint8_t ch);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for reading in various parameters
|
||||
|
||||
// ================================================================
|
||||
// c_get_symbol_val ()
|
||||
// Returns the value of a symbol (a memory address) from a symbol-table file.
|
||||
// The symbol-table file has a '<symbol> <value-in-hex>' pair on each line.
|
||||
// Reads the whole symbol-table file on each call,
|
||||
// which is ok if it's not called often and the file is small.
|
||||
|
||||
extern
|
||||
uint64_t c_get_symbol_val (char * symbol);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for Tandem Verification trace file output.
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_open()
|
||||
// Open file for recording binary trace output.
|
||||
|
||||
extern
|
||||
uint32_t c_trace_file_open (uint8_t dummy);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_byte_in_buffer ()
|
||||
// Write 8-bit 'data' into output buffer at byte offset 'j'
|
||||
|
||||
extern
|
||||
uint32_t c_trace_file_load_byte_in_buffer (uint32_t j, uint8_t data);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_word64_in_buffer ()
|
||||
// Write 64-bit 'data' into output buffer at 'byte_offset'
|
||||
|
||||
extern
|
||||
uint32_t c_trace_file_load_word64_in_buffer (uint32_t byte_offset, uint64_t data);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_write_buffer()
|
||||
// Write out 'n' bytes from the already-loaded output buffer to the trace file.
|
||||
|
||||
extern
|
||||
uint32_t c_trace_file_write_buffer (uint32_t n);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_close()
|
||||
// Close the trace file.
|
||||
|
||||
extern
|
||||
uint32_t c_trace_file_close (uint8_t dummy);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for communication with remote debug client.
|
||||
|
||||
// ================================================================
|
||||
|
||||
#define DMI_OP_READ 1
|
||||
#define DMI_OP_WRITE 2
|
||||
#define DMI_OP_SHUTDOWN 3
|
||||
#define DMI_OP_START_COMMAND 4
|
||||
|
||||
#define DMI_STATUS_ERR 0
|
||||
#define DMI_STATUS_OK 1
|
||||
#define DMI_STATUS_UNAVAIL 2
|
||||
|
||||
extern
|
||||
uint8_t c_debug_client_connect (const uint16_t tcp_port);
|
||||
|
||||
extern
|
||||
uint8_t c_debug_client_disconnect (uint8_t dummy);
|
||||
|
||||
extern
|
||||
uint64_t c_debug_client_request_recv (uint8_t dummy);
|
||||
|
||||
extern
|
||||
uint8_t c_debug_client_response_send (const uint32_t data);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
155
src_Testbench/Top/C_Imports.bsv
Normal file
155
src_Testbench/Top/C_Imports.bsv
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package C_Imports;
|
||||
|
||||
// ================================================================
|
||||
// These are functions imported into BSV during Bluesim or Verilog simulation.
|
||||
// See C_Imported_Functions.{h,c} for the corresponding C declarations
|
||||
// and implementations.
|
||||
|
||||
// There are several independent groups of functions below; the
|
||||
// groups are separated by heavy dividers ('// *******')
|
||||
|
||||
// Below, 'dummy' args are not used, and are present only to appease
|
||||
// some Verilog simulators that are finicky about 0-arg functions.
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Vector :: *;
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for console I/O
|
||||
|
||||
// ================================================================
|
||||
// c_trygetchar ()
|
||||
// Returns next input character (ASCII code) from the console.
|
||||
// Returns 0 if no input is available.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(8)) c_trygetchar (Bit #(8) dummy);
|
||||
|
||||
// ================================================================
|
||||
// c_putchar ()
|
||||
// Writes character to stdout
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_putchar (Bit #(8) ch);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for reading in various parameters
|
||||
|
||||
// ================================================================
|
||||
// c_get_symbol_val ()
|
||||
// Returns the value of a symbol (a memory address) from a symbol-table file.
|
||||
// The symbol-table file has a '<symbol> <value-in-hex>' pair on each line.
|
||||
// Reads the whole symbol-table file on each call,
|
||||
// which is ok if it's not called often and the file is small.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(64)) c_get_symbol_val (String symbol);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for Tandem Verification trace file output.
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_open ()
|
||||
// Open file for recording binary trace output.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_trace_file_open (Bit #(8) dummy);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_byte_in_buffer ()
|
||||
// Write 8-bit 'data' into output buffer at byte offset 'j'
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_trace_file_load_byte_in_buffer (Bit #(32) j, Bit #(8) data);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_load_word64_in_buffer ()
|
||||
// Write 64-bit 'data' into output buffer at 'byte_offset'
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_trace_file_load_word64_in_buffer (Bit #(32) byte_offset, Bit #(64) data);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_write_buffer ()
|
||||
// Write out 'n' bytes from the already-loaded output buffer to the trace file.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_trace_file_write_buffer (Bit #(32) n);
|
||||
|
||||
// ================================================================
|
||||
// c_trace_file_close()
|
||||
// Close the trace file.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(32)) c_trace_file_close (Bit #(8) dummy);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// Functions for communication with remote debug client.
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
// ================================================================
|
||||
// Commands in requests.
|
||||
|
||||
Bit #(16) dmi_default_tcp_port = 30000;
|
||||
|
||||
Bit #(8) dmi_status_err = 0;
|
||||
Bit #(8) dmi_status_ok = 1;
|
||||
Bit #(8) dmi_status_unavail = 2;
|
||||
|
||||
Bit #(8) dmi_op_read = 1;
|
||||
Bit #(8) dmi_op_write = 2;
|
||||
Bit #(8) dmi_op_shutdown = 3;
|
||||
Bit #(8) dmi_op_start_command = 4;
|
||||
|
||||
// ================================================================
|
||||
// Connect to debug client as server on tcp_port.
|
||||
// Return fail/ok.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(8)) c_debug_client_connect (Bit #(16) tcp_port);
|
||||
|
||||
// ================================================================
|
||||
// Disconnect from debug client as server.
|
||||
// Return fail/ok.
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(8)) c_debug_client_disconnect (Bit #(8) dummy);
|
||||
|
||||
// ================================================================
|
||||
// Receive 7-byte request from debug client
|
||||
// Result is: { status, data_b3, data_b2, data_b1, data_b0, addr_b1, addr_b0, op }
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(64)) c_debug_client_request_recv (Bit #(8) dummy);
|
||||
|
||||
// ================================================================
|
||||
// Send 4-byte response 'data' to debug client.
|
||||
// Returns fail/ok status
|
||||
|
||||
import "BDPI"
|
||||
function ActionValue #(Bit #(8)) c_debug_client_response_send (Bit #(32) data);
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
|
||||
endpackage
|
||||
37
src_Testbench/Top/Makefile
Normal file
37
src_Testbench/Top/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
# This Makefile is for standalone testing of various groups of
|
||||
# functions in C_Imported_Functions.c
|
||||
|
||||
# ================================================================
|
||||
|
||||
C_Imported_Functions.o: C_Imported_Functions.h C_Imported_Functions.c
|
||||
$(CC) -c C_Imported_Functions.c
|
||||
|
||||
# ================================================================
|
||||
# Standalone testing
|
||||
|
||||
# Uncomment one of the following
|
||||
|
||||
TEST ?= PLEASE_CHOOSE_TEST
|
||||
|
||||
# TEST = TEST_TRYGETCHAR
|
||||
# TEST = TEST_GET_SYMBOL_VAL
|
||||
TEST = TEST_COMMS
|
||||
|
||||
test_exe: C_Imported_Functions.c
|
||||
$(CC) -o test_exe -D$(TEST) C_Imported_Functions.c
|
||||
|
||||
.PHONY: start
|
||||
start: test_exe
|
||||
./test_exe
|
||||
|
||||
# ================================================================
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *~ *.o
|
||||
|
||||
.PHONY: full_clean
|
||||
full_clean:
|
||||
rm -f *~ *.o test_exe
|
||||
|
||||
# ================================================================
|
||||
84
src_Testbench/Top/Mem_Model.bsv
Normal file
84
src_Testbench/Top/Mem_Model.bsv
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Mem_Model;
|
||||
|
||||
// ================================================================
|
||||
// A simulation model of external DRAM memory.
|
||||
// Uses a register file to model memory.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import RegFile :: *;
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Memory :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import Mem_Controller :: *;
|
||||
|
||||
// ================================================================
|
||||
// Mem Model interface
|
||||
|
||||
interface Mem_Model_IFC;
|
||||
// The read/write interface
|
||||
interface MemoryServer #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word) mem_server;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// Mem Model implementation
|
||||
|
||||
(* synthesize *)
|
||||
module mkMem_Model (Mem_Model_IFC);
|
||||
|
||||
Integer verbosity = 0; // 0 = quiet; 1 = verbose
|
||||
|
||||
Raw_Mem_Addr alloc_size = 'h_80_0000; // 8M raw mem words, or 256MB
|
||||
|
||||
RegFile #(Raw_Mem_Addr, Bit #(Bits_per_Raw_Mem_Word)) rf <- mkRegFileLoad ("Mem.hex", 0, alloc_size - 1);
|
||||
|
||||
FIFOF #(MemoryResponse #(Bits_per_Raw_Mem_Word)) f_raw_mem_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
interface MemoryServer mem_server;
|
||||
interface Put request;
|
||||
method Action put (MemoryRequest #(Bits_per_Raw_Mem_Addr, Bits_per_Raw_Mem_Word) req);
|
||||
if (req.address >= alloc_size) begin
|
||||
$display ("%0d: ERROR: Mem_Model.request.put: addr 0x%0h >= size 0x%0h (num raw-mem words)",
|
||||
cur_cycle, req.address, alloc_size);
|
||||
$finish (1); // Assertion failure: address out of bounds
|
||||
end
|
||||
else if (req.write) begin
|
||||
rf.upd (req.address, req.data);
|
||||
if (verbosity != 0)
|
||||
$display ("%0d: Mem_Model write [0x%0h] <= 0x%0h", cur_cycle, req.address, req.data);
|
||||
end
|
||||
else begin
|
||||
let x = rf.sub (req.address);
|
||||
let rsp = MemoryResponse {data: x};
|
||||
f_raw_mem_rsps.enq (rsp);
|
||||
if (verbosity != 0)
|
||||
$display ("%0d: Mem_Model read [0x%0h] => 0x%0h", cur_cycle, req.address, x);
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface Get response = toGet (f_raw_mem_rsps);
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
269
src_Testbench/Top/Top_HW_Side.bsv
Normal file
269
src_Testbench/Top/Top_HW_Side.bsv
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package Top_HW_Side;
|
||||
|
||||
// ================================================================
|
||||
// mkTop_HW_Side is the top-level system for simulation.
|
||||
// mkMem_Model is a memory model.
|
||||
|
||||
// **** CAVEAT FOR IVERILOG USERS: The 'C_Imports' sections below are
|
||||
// disabled for IVerilog until we find a clean solution. They depend
|
||||
// on imported C which is non-trivial in IVerilog because IVerilog
|
||||
// still depends on the older Verilog VPI standard instead of the
|
||||
// newer DPI-C standard. C-imported functions are used for:
|
||||
// UART input polling and character-reading
|
||||
// Writing tandem-verfication encoded trace data
|
||||
|
||||
// (Note: UART output does not depend on C-imported functions and so
|
||||
// will work ok even in IVerilog)
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TV_Info :: *;
|
||||
import SoC_Top :: *;
|
||||
import Mem_Controller :: *;
|
||||
import Mem_Model :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
import PLIC :: *;
|
||||
|
||||
`ifndef IVERILOG
|
||||
import C_Imports :: *;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
import External_Control :: *;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Top-level module.
|
||||
// Instantiates the SoC.
|
||||
// Instantiates a memory model.
|
||||
|
||||
(* synthesize *)
|
||||
module mkTop_HW_Side (Empty) ;
|
||||
|
||||
SoC_Top_IFC soc_top <- mkSoC_Top;
|
||||
Mem_Model_IFC mem_model <- mkMem_Model;
|
||||
|
||||
// Connect SoC to raw memory
|
||||
let memCnx <- mkConnection (soc_top.to_raw_mem, mem_model.mem_server);
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
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
|
||||
|
||||
// ----------------
|
||||
// Open file for Tandem Verification trace output
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
`ifndef IVERILOG
|
||||
// Note: see 'CAVEAT FOR IVERILOG USERS' above
|
||||
let success <- c_trace_file_open ('h_AA);
|
||||
if (success == 0) begin
|
||||
$display ("ERROR: Top_HW_Side.rl_step0: error opening trace file.");
|
||||
$display (" Aborting.");
|
||||
$finish (1);
|
||||
end
|
||||
else
|
||||
$display ("Top_HW_Side.rl_step0: opened trace file.");
|
||||
`else
|
||||
$display ("Warning: tandem verification output logs not available in IVerilog");
|
||||
`endif
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Open connection to remote debug client
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
`ifndef IVERILOG
|
||||
// Note: see 'CAVEAT FOR IVERILOG USERS' above
|
||||
let dmi_status <- c_debug_client_connect (dmi_default_tcp_port);
|
||||
if (dmi_status != dmi_status_ok) begin
|
||||
$display ("ERROR: Top_HW_Side.rl_step0: error opening debug client connection.");
|
||||
$display (" Aborting.");
|
||||
$finish (1);
|
||||
end
|
||||
`else
|
||||
$display ("Warning: Debug client connection not available in IVerilog");
|
||||
`endif
|
||||
`endif
|
||||
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Tandem verifier: drain and output vectors of bytes
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
rule rl_tv_vb_out;
|
||||
let tv_info <- soc_top.tv_verifier_info_get.get;
|
||||
let n = tv_info.num_bytes;
|
||||
let vb = tv_info.vec_bytes;
|
||||
|
||||
`ifndef IVERILOG
|
||||
Bit #(32) success = 1;
|
||||
|
||||
for (Bit #(32) j = 0; j < fromInteger (valueOf (TV_VB_SIZE)); j = j + 8) begin
|
||||
Bit #(64) w64 = { vb [j+7], vb [j+6], vb [j+5], vb [j+4], vb [j+3], vb [j+2], vb [j+1], vb [j] };
|
||||
let success1 <- c_trace_file_load_word64_in_buffer (j, w64);
|
||||
end
|
||||
|
||||
if (success == 0)
|
||||
$display ("ERROR: Top_HW_Side.rl_tv_vb_out: error loading %0d bytes into buffer", n);
|
||||
else begin
|
||||
// Send the data
|
||||
success <- c_trace_file_write_buffer (n);
|
||||
if (success == 0)
|
||||
$display ("ERROR: Top_HW_Side.rl_tv_vb_out: error writing out bytevec data buffer (%0d bytes)", n);
|
||||
end
|
||||
|
||||
if (success == 0) begin
|
||||
$finish (1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// UART console I/O
|
||||
|
||||
// Relay system console output to terminal
|
||||
|
||||
rule rl_relay_console_out;
|
||||
let ch <- soc_top.get_to_console.get;
|
||||
$write ("%c", ch);
|
||||
$fflush (stdout);
|
||||
endrule
|
||||
|
||||
// Poll terminal input and relay any chars into system console input.
|
||||
// Note: rg_console_in_poll is used to poll only every N cycles, whenever it wraps around to 0.
|
||||
// Note: see 'CAVEAT FOR IVERILOG USERS' above for why this is ifdef'd out for iVerilog users.
|
||||
|
||||
`ifndef IVERILOG
|
||||
|
||||
Reg #(Bit #(12)) rg_console_in_poll <- mkReg (0);
|
||||
|
||||
rule rl_relay_console_in;
|
||||
if (rg_console_in_poll == 0) begin
|
||||
Bit #(8) ch <- c_trygetchar (?);
|
||||
if (ch != 0) begin
|
||||
soc_top.put_from_console.put (ch);
|
||||
/*
|
||||
$write ("%0d: Top_HW_Side.bsv.rl_relay_console: ch = 0x%0h", cur_cycle, ch);
|
||||
if (ch >= 'h20) $write (" ('%c')", ch);
|
||||
$display ("");
|
||||
*/
|
||||
end
|
||||
end
|
||||
rg_console_in_poll <= rg_console_in_poll + 1;
|
||||
endrule
|
||||
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Interaction with remote debug client
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
rule rl_debug_client_request_recv;
|
||||
Bit #(64) req <- c_debug_client_request_recv ('hAA);
|
||||
Bit #(8) status = req [63:56];
|
||||
Bit #(32) data = req [55:24];
|
||||
Bit #(16) addr = req [23:8];
|
||||
Bit #(8) op = req [7:0];
|
||||
if (status == dmi_status_err) begin
|
||||
$display ("%0d: Top_HW_Side.rl_debug_client_request_recv: receive error; aborting",
|
||||
cur_cycle);
|
||||
$finish (1);
|
||||
end
|
||||
else if (status == dmi_status_ok) begin
|
||||
// $write ("%0d: Top_HW_Side.rl_debug_client_request_recv:", cur_cycle);
|
||||
if (op == dmi_op_read) begin
|
||||
// $display (" READ 0x%0h", addr);
|
||||
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);
|
||||
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);
|
||||
end
|
||||
else if (op == dmi_op_shutdown) begin
|
||||
$display ("Top_HW_Side.rl_debug_client_request_recv: SHUTDOWN");
|
||||
$finish (0);
|
||||
end
|
||||
else if (op == dmi_op_start_command) begin // For debugging only
|
||||
// $display (" START COMMAND ================================");
|
||||
end
|
||||
else
|
||||
$display (" Top_HW_Side.rl_debug_client_request_recv: UNRECOGNIZED OP %0d; ignoring", op);
|
||||
end
|
||||
endrule
|
||||
|
||||
rule rl_debug_client_response_send;
|
||||
let control_rsp <- soc_top.server_external_control.response.get;
|
||||
// $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
|
||||
$display ("%0d: Top_HW_Side.rl_debug_client_response_send: send error; aborting",
|
||||
cur_cycle);
|
||||
$finish (1);
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// None (this is top-level)
|
||||
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage: Top_HW_Side
|
||||
Reference in New Issue
Block a user