Initial load of files

This commit is contained in:
rsnikhil
2019-03-26 14:49:40 -04:00
parent bc62f17032
commit ee24a93944
1008 changed files with 354221 additions and 224 deletions

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View File

@@ -0,0 +1 @@
*.exe

View 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))

View 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

View 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);
}
// ================================================================

View 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

View 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

View 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

View 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

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
// ****************************************************************
// ****************************************************************
// ****************************************************************

View 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

View 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

View 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
# ================================================================

View 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

View 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