Compare commits
6 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c8144787b0 | 4 months ago |
|
|
82fe15aa3e | 8 months ago |
|
|
6b950d2ea0 | 8 months ago |
|
|
a60237e7fd | 8 months ago |
|
|
6293d1cd89 | 8 months ago |
|
|
2f2f9e9b54 | 9 months ago |
@ -1,3 +1,4 @@
|
||||
.vscode/
|
||||
.metals/
|
||||
.VSCodeCounter/
|
||||
.DS_Store
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
module top(
|
||||
input clock,
|
||||
input reset,
|
||||
// Interrupts
|
||||
input mei, // to PLIC
|
||||
input msi, // to CLINT
|
||||
input mti, // to CLINT
|
||||
input sei, // to PLIC
|
||||
// aw
|
||||
output [3:0]axi_awid,
|
||||
output[31:0]axi_awaddr,
|
||||
output [7:0]axi_awlen,
|
||||
output [2:0]axi_awsize,
|
||||
output [1:0]axi_awburst,
|
||||
output axi_awvalid,
|
||||
input axi_awready,
|
||||
// w
|
||||
output[63:0]axi_wdata,
|
||||
output [7:0]axi_wstrb,
|
||||
output axi_wlast,
|
||||
output axi_wvalid,
|
||||
input axi_wready,
|
||||
// b
|
||||
input [3:0]axi_bid,
|
||||
input [1:0]axi_bresp,
|
||||
input axi_bvalid,
|
||||
output axi_bready,
|
||||
// ar
|
||||
output [3:0]axi_arid,
|
||||
output[31:0]axi_araddr,
|
||||
output [7:0]axi_arlen,
|
||||
output [2:0]axi_arsize,
|
||||
output [1:0]axi_arburst,
|
||||
output axi_arvalid,
|
||||
input axi_arready,
|
||||
// r
|
||||
input [3:0]axi_rid,
|
||||
input [63:0]axi_rdata,
|
||||
input [1:0]axi_rresp,
|
||||
input axi_rlast,
|
||||
input axi_rvalid,
|
||||
output axi_rready,
|
||||
// debug
|
||||
output debug_commit,
|
||||
output[63:0]debug_pc,
|
||||
output[4:0] debug_rf_wnum,
|
||||
output[63:0]debug_rf_wdata
|
||||
);
|
||||
|
||||
PuaCpu core(
|
||||
.clock (clock),
|
||||
.reset (reset),
|
||||
// Interrupts
|
||||
.io_ext_int_mei (mei), // to PLIC
|
||||
.io_ext_int_msi (msi), // to CLINT
|
||||
.io_ext_int_mti (mti), // to CLINT
|
||||
.io_ext_int_sei (sei), // to PLIC
|
||||
// aw
|
||||
.io_axi_aw_bits_id (axi_awid),
|
||||
.io_axi_aw_bits_addr (axi_awaddr),
|
||||
.io_axi_aw_bits_len (axi_awlen),
|
||||
.io_axi_aw_bits_size (axi_awsize),
|
||||
.io_axi_aw_bits_burst (axi_awburst),
|
||||
.io_axi_aw_valid (axi_awvalid),
|
||||
.io_axi_aw_ready (axi_awready),
|
||||
// w
|
||||
.io_axi_w_bits_data (axi_wdata),
|
||||
.io_axi_w_bits_strb (axi_wstrb),
|
||||
.io_axi_w_bits_last (axi_wlast),
|
||||
.io_axi_w_valid (axi_wvalid),
|
||||
.io_axi_w_ready (axi_wready),
|
||||
// b
|
||||
.io_axi_b_bits_id (axi_bid),
|
||||
.io_axi_b_bits_resp (axi_bresp),
|
||||
.io_axi_b_valid (axi_bvalid),
|
||||
.io_axi_b_ready (axi_bready),
|
||||
// ar
|
||||
.io_axi_ar_bits_id (axi_arid),
|
||||
.io_axi_ar_bits_addr (axi_araddr),
|
||||
.io_axi_ar_bits_len (axi_arlen),
|
||||
.io_axi_ar_bits_size (axi_arsize),
|
||||
.io_axi_ar_bits_burst (axi_arburst),
|
||||
.io_axi_ar_valid (axi_arvalid),
|
||||
.io_axi_ar_ready (axi_arready),
|
||||
// r
|
||||
.io_axi_r_bits_id (axi_rid),
|
||||
.io_axi_r_bits_data (axi_rdata),
|
||||
.io_axi_r_bits_resp (axi_rresp),
|
||||
.io_axi_r_bits_last (axi_rlast),
|
||||
.io_axi_r_valid (axi_rvalid),
|
||||
.io_axi_r_ready (axi_rready),
|
||||
// debug
|
||||
.io_debug_pc (debug_pc),
|
||||
.io_debug_commit (debug_commit),
|
||||
.io_debug_rf_wnum (debug_rf_wnum),
|
||||
.io_debug_rf_wdata (debug_rf_wdata)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@ -0,0 +1,54 @@
|
||||
module top(
|
||||
input clock,
|
||||
input reset,
|
||||
// Interrupts
|
||||
input mei, // to PLIC
|
||||
input msi, // to CLINT
|
||||
input mti, // to CLINT
|
||||
input sei, // to PLIC
|
||||
// inst sram interface
|
||||
output inst_sram_en,
|
||||
output [ 3:0] inst_sram_wen,
|
||||
output [31:0] inst_sram_addr,
|
||||
output [31:0] inst_sram_wdata,
|
||||
input [31:0] inst_sram_rdata,
|
||||
// data sram interface
|
||||
output data_sram_en,
|
||||
output [ 7:0] data_sram_wen,
|
||||
output [31:0] data_sram_addr,
|
||||
output [63:0] data_sram_wdata,
|
||||
input [63:0] data_sram_rdata,
|
||||
// trace debug interface
|
||||
output debug_commit,
|
||||
output [63:0] debug_pc,
|
||||
output [4:0 ] debug_rf_wnum,
|
||||
output [63:0] debug_rf_wdata
|
||||
);
|
||||
|
||||
PuaCpu core(
|
||||
.clock (clock),
|
||||
.reset (reset),
|
||||
// interrupts
|
||||
.io_ext_int_mei (mei),
|
||||
.io_ext_int_mti (mti),
|
||||
.io_ext_int_msi (msi),
|
||||
// inst sram interface
|
||||
.io_inst_sram_en (inst_sram_en),
|
||||
.io_inst_sram_wen (inst_sram_wen),
|
||||
.io_inst_sram_addr (inst_sram_addr),
|
||||
.io_inst_sram_wdata (inst_sram_wdata),
|
||||
.io_inst_sram_rdata (inst_sram_rdata),
|
||||
// data sram interface
|
||||
.io_data_sram_en (data_sram_en),
|
||||
.io_data_sram_wen (data_sram_wen),
|
||||
.io_data_sram_addr (data_sram_addr),
|
||||
.io_data_sram_wdata (data_sram_wdata),
|
||||
.io_data_sram_rdata (data_sram_rdata),
|
||||
// debug
|
||||
.io_debug_pc (debug_pc),
|
||||
.io_debug_commit (debug_commit),
|
||||
.io_debug_rf_wnum (debug_rf_wnum),
|
||||
.io_debug_rf_wdata (debug_rf_wdata)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@ -1,99 +0,0 @@
|
||||
module top(
|
||||
input clock,
|
||||
input reset,
|
||||
// Interrupts
|
||||
input mei, // to PLIC
|
||||
input msi, // to CLINT
|
||||
input mti, // to CLINT
|
||||
input sei, // to PLIC
|
||||
// aw
|
||||
output [3:0]axi_awid,
|
||||
output[31:0]axi_awaddr,
|
||||
output [7:0]axi_awlen,
|
||||
output [2:0]axi_awsize,
|
||||
output [1:0]axi_awburst,
|
||||
output axi_awvalid,
|
||||
input axi_awready,
|
||||
// w
|
||||
output[63:0]axi_wdata,
|
||||
output [7:0]axi_wstrb,
|
||||
output axi_wlast,
|
||||
output axi_wvalid,
|
||||
input axi_wready,
|
||||
// b
|
||||
input [3:0]axi_bid,
|
||||
input [1:0]axi_bresp,
|
||||
input axi_bvalid,
|
||||
output axi_bready,
|
||||
// ar
|
||||
output [3:0]axi_arid,
|
||||
output[31:0]axi_araddr,
|
||||
output [7:0]axi_arlen,
|
||||
output [2:0]axi_arsize,
|
||||
output [1:0]axi_arburst,
|
||||
output axi_arvalid,
|
||||
input axi_arready,
|
||||
// r
|
||||
input [3:0]axi_rid,
|
||||
input [63:0]axi_rdata,
|
||||
input [1:0]axi_rresp,
|
||||
input axi_rlast,
|
||||
input axi_rvalid,
|
||||
output axi_rready,
|
||||
// debug
|
||||
output debug_commit,
|
||||
output[63:0]debug_pc,
|
||||
output[4:0] debug_rf_wnum,
|
||||
output[63:0]debug_rf_wdata
|
||||
);
|
||||
|
||||
PuaCpu core(
|
||||
.clock (clock),
|
||||
.reset (reset),
|
||||
// Interrupts
|
||||
.io_ext_int_mei (mei), // to PLIC
|
||||
.io_ext_int_msi (msi), // to CLINT
|
||||
.io_ext_int_mti (mti), // to CLINT
|
||||
.io_ext_int_sei (sei), // to PLIC
|
||||
// aw
|
||||
.io_axi_aw_bits_id (axi_awid),
|
||||
.io_axi_aw_bits_addr (axi_awaddr),
|
||||
.io_axi_aw_bits_len (axi_awlen),
|
||||
.io_axi_aw_bits_size (axi_awsize),
|
||||
.io_axi_aw_bits_burst (axi_awburst),
|
||||
.io_axi_aw_valid (axi_awvalid),
|
||||
.io_axi_aw_ready (axi_awready),
|
||||
// w
|
||||
.io_axi_w_bits_data (axi_wdata),
|
||||
.io_axi_w_bits_strb (axi_wstrb),
|
||||
.io_axi_w_bits_last (axi_wlast),
|
||||
.io_axi_w_valid (axi_wvalid),
|
||||
.io_axi_w_ready (axi_wready),
|
||||
// b
|
||||
.io_axi_b_bits_id (axi_bid),
|
||||
.io_axi_b_bits_resp (axi_bresp),
|
||||
.io_axi_b_valid (axi_bvalid),
|
||||
.io_axi_b_ready (axi_bready),
|
||||
// ar
|
||||
.io_axi_ar_bits_id (axi_arid),
|
||||
.io_axi_ar_bits_addr (axi_araddr),
|
||||
.io_axi_ar_bits_len (axi_arlen),
|
||||
.io_axi_ar_bits_size (axi_arsize),
|
||||
.io_axi_ar_bits_burst (axi_arburst),
|
||||
.io_axi_ar_valid (axi_arvalid),
|
||||
.io_axi_ar_ready (axi_arready),
|
||||
// r
|
||||
.io_axi_r_bits_id (axi_rid),
|
||||
.io_axi_r_bits_data (axi_rdata),
|
||||
.io_axi_r_bits_resp (axi_rresp),
|
||||
.io_axi_r_bits_last (axi_rlast),
|
||||
.io_axi_r_valid (axi_rvalid),
|
||||
.io_axi_r_ready (axi_rready),
|
||||
// debug
|
||||
.io_debug_pc (debug_pc),
|
||||
.io_debug_commit (debug_commit),
|
||||
.io_debug_rf_wnum (debug_rf_wnum),
|
||||
.io_debug_rf_wdata (debug_rf_wdata)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@ -0,0 +1,54 @@
|
||||
module top(
|
||||
input clock,
|
||||
input reset,
|
||||
// Interrupts
|
||||
input mei, // to PLIC
|
||||
input msi, // to CLINT
|
||||
input mti, // to CLINT
|
||||
input sei, // to PLIC
|
||||
// inst sram interface
|
||||
output inst_sram_en,
|
||||
output [ 3:0] inst_sram_wen,
|
||||
output [31:0] inst_sram_addr,
|
||||
output [31:0] inst_sram_wdata,
|
||||
input [31:0] inst_sram_rdata,
|
||||
// data sram interface
|
||||
output data_sram_en,
|
||||
output [ 7:0] data_sram_wen,
|
||||
output [31:0] data_sram_addr,
|
||||
output [63:0] data_sram_wdata,
|
||||
input [63:0] data_sram_rdata,
|
||||
// trace debug interface
|
||||
output debug_commit,
|
||||
output [63:0] debug_pc,
|
||||
output [4:0 ] debug_rf_wnum,
|
||||
output [63:0] debug_rf_wdata
|
||||
);
|
||||
|
||||
PuaCpu core(
|
||||
.clock (clock),
|
||||
.reset (reset),
|
||||
// interrupts
|
||||
.io_ext_int_mei (mei),
|
||||
.io_ext_int_mti (mti),
|
||||
.io_ext_int_msi (msi),
|
||||
// inst sram interface
|
||||
.io_inst_sram_en (inst_sram_en),
|
||||
.io_inst_sram_wen (inst_sram_wen),
|
||||
.io_inst_sram_addr (inst_sram_addr),
|
||||
.io_inst_sram_wdata (inst_sram_wdata),
|
||||
.io_inst_sram_rdata (inst_sram_rdata),
|
||||
// data sram interface
|
||||
.io_data_sram_en (data_sram_en),
|
||||
.io_data_sram_wen (data_sram_wen),
|
||||
.io_data_sram_addr (data_sram_addr),
|
||||
.io_data_sram_wdata (data_sram_wdata),
|
||||
.io_data_sram_rdata (data_sram_rdata),
|
||||
// debug
|
||||
.io_debug_pc (debug_pc),
|
||||
.io_debug_commit (debug_commit),
|
||||
.io_debug_rf_wnum (debug_rf_wnum),
|
||||
.io_debug_rf_wdata (debug_rf_wdata)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@ -1,312 +0,0 @@
|
||||
#ifndef AXI4_HPP
|
||||
#define AXI4_HPP
|
||||
|
||||
#include <verilated.h>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <cstring>
|
||||
|
||||
#define AUTO_SIG(name, msb, lsb) \
|
||||
typename std::conditional<(msb - lsb + 1) <= 8, CData, \
|
||||
typename std::conditional<(msb - lsb + 1) <= 16, SData, \
|
||||
typename std::conditional<(msb - lsb + 1) <= 32, IData, QData>::type>::type>::type name
|
||||
|
||||
#define AUTO_IN(name, msb, lsb) AUTO_SIG(name, msb, lsb)
|
||||
#define AUTO_OUT(name, msb, lsb) AUTO_SIG(name, msb, lsb)
|
||||
|
||||
/*
|
||||
We have defined 3 types of AXI signals for a different purposes: axi4, axi4_ptr, axi4_ref.
|
||||
|
||||
Since verilator exposes signals as a value itself, we use axi4_ptr to get signal to connect.
|
||||
|
||||
Then axi4_ptr can be converted to axi4_ref to reach the value in a better way.
|
||||
*/
|
||||
|
||||
template <unsigned int A_WIDTH, unsigned int D_WIDTH, unsigned int ID_WIDTH>
|
||||
struct axi4;
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
struct axi4_ptr
|
||||
{
|
||||
static_assert(__builtin_popcount(D_WIDTH) == 1, "D_WIDTH should be the power of 2.");
|
||||
static_assert(D_WIDTH >= 8, "D_WIDTH should be larger or equal to 8.");
|
||||
// aw
|
||||
AUTO_IN(*awid, ID_WIDTH - 1, 0) = NULL;
|
||||
AUTO_IN(*awaddr, A_WIDTH - 1, 0) = NULL;
|
||||
AUTO_IN(*awlen, 7, 0) = NULL;
|
||||
AUTO_IN(*awsize, 2, 0) = NULL;
|
||||
AUTO_IN(*awburst, 1, 0) = NULL;
|
||||
AUTO_IN(*awvalid, 0, 0) = NULL;
|
||||
AUTO_OUT(*awready, 0, 0) = NULL;
|
||||
// w
|
||||
AUTO_IN(*wdata, D_WIDTH - 1, 0) = NULL;
|
||||
AUTO_IN(*wstrb, (D_WIDTH / 8) - 1, 0) = NULL;
|
||||
AUTO_IN(*wlast, 0, 0) = NULL;
|
||||
AUTO_IN(*wvalid, 0, 0) = NULL;
|
||||
AUTO_OUT(*wready, 0, 0) = NULL;
|
||||
// b
|
||||
AUTO_OUT(*bid, ID_WIDTH - 1, 0) = NULL;
|
||||
AUTO_OUT(*bresp, 1, 0) = NULL;
|
||||
AUTO_OUT(*bvalid, 0, 0) = NULL;
|
||||
AUTO_IN(*bready, 0, 0) = NULL;
|
||||
// ar
|
||||
AUTO_IN(*arid, ID_WIDTH - 1, 0) = NULL;
|
||||
AUTO_IN(*araddr, A_WIDTH - 1, 0) = NULL;
|
||||
AUTO_IN(*arlen, 7, 0) = NULL;
|
||||
AUTO_IN(*arsize, 2, 0) = NULL;
|
||||
AUTO_IN(*arburst, 1, 0) = NULL;
|
||||
AUTO_IN(*arvalid, 0, 0) = NULL;
|
||||
AUTO_OUT(*arready, 0, 0) = NULL;
|
||||
// r
|
||||
AUTO_OUT(*rid, ID_WIDTH - 1, 0) = NULL;
|
||||
AUTO_OUT(*rdata, D_WIDTH - 1, 0) = NULL;
|
||||
AUTO_OUT(*rresp, 1, 0) = NULL;
|
||||
AUTO_OUT(*rlast, 0, 0) = NULL;
|
||||
AUTO_OUT(*rvalid, 0, 0) = NULL;
|
||||
AUTO_IN(*rready, 0, 0) = NULL;
|
||||
bool check()
|
||||
{
|
||||
std::set<void *> s;
|
||||
// aw
|
||||
s.insert((void *)awid);
|
||||
s.insert((void *)awaddr);
|
||||
s.insert((void *)awlen);
|
||||
s.insert((void *)awsize);
|
||||
s.insert((void *)awburst);
|
||||
s.insert((void *)awvalid);
|
||||
s.insert((void *)awready);
|
||||
// w
|
||||
s.insert((void *)wdata);
|
||||
s.insert((void *)wstrb);
|
||||
s.insert((void *)wlast);
|
||||
s.insert((void *)wvalid);
|
||||
s.insert((void *)wready);
|
||||
// b
|
||||
s.insert((void *)bid);
|
||||
s.insert((void *)bresp);
|
||||
s.insert((void *)bvalid);
|
||||
s.insert((void *)bready);
|
||||
// ar
|
||||
s.insert((void *)arid);
|
||||
s.insert((void *)araddr);
|
||||
s.insert((void *)arlen);
|
||||
s.insert((void *)arsize);
|
||||
s.insert((void *)arburst);
|
||||
s.insert((void *)arvalid);
|
||||
s.insert((void *)arready);
|
||||
// r
|
||||
s.insert((void *)rid);
|
||||
s.insert((void *)rdata);
|
||||
s.insert((void *)rresp);
|
||||
s.insert((void *)rlast);
|
||||
s.insert((void *)rvalid);
|
||||
s.insert((void *)rready);
|
||||
return s.size() == 29 && s.count(NULL) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
struct axi4_ref
|
||||
{
|
||||
AUTO_IN(&awid, ID_WIDTH - 1, 0);
|
||||
AUTO_IN(&awaddr, A_WIDTH - 1, 0);
|
||||
AUTO_IN(&awlen, 7, 0);
|
||||
AUTO_IN(&awsize, 2, 0);
|
||||
AUTO_IN(&awburst, 1, 0);
|
||||
AUTO_IN(&awvalid, 0, 0);
|
||||
AUTO_OUT(&awready, 0, 0);
|
||||
// w
|
||||
AUTO_IN(&wdata, D_WIDTH - 1, 0);
|
||||
AUTO_IN(&wstrb, (D_WIDTH / 8) - 1, 0);
|
||||
AUTO_IN(&wlast, 0, 0);
|
||||
AUTO_IN(&wvalid, 0, 0);
|
||||
AUTO_OUT(&wready, 0, 0);
|
||||
// b
|
||||
AUTO_OUT(&bid, ID_WIDTH - 1, 0);
|
||||
AUTO_OUT(&bresp, 1, 0);
|
||||
AUTO_OUT(&bvalid, 0, 0);
|
||||
AUTO_IN(&bready, 0, 0);
|
||||
// ar
|
||||
AUTO_IN(&arid, ID_WIDTH - 1, 0);
|
||||
AUTO_IN(&araddr, A_WIDTH - 1, 0);
|
||||
AUTO_IN(&arlen, 7, 0);
|
||||
AUTO_IN(&arsize, 2, 0);
|
||||
AUTO_IN(&arburst, 1, 0);
|
||||
AUTO_IN(&arvalid, 0, 0);
|
||||
AUTO_OUT(&arready, 0, 0);
|
||||
// r
|
||||
AUTO_OUT(&rid, ID_WIDTH - 1, 0);
|
||||
AUTO_OUT(&rdata, D_WIDTH - 1, 0);
|
||||
AUTO_OUT(&rresp, 1, 0);
|
||||
AUTO_OUT(&rlast, 0, 0);
|
||||
AUTO_OUT(&rvalid, 0, 0);
|
||||
AUTO_IN(&rready, 0, 0);
|
||||
axi4_ref(axi4_ptr<A_WIDTH, D_WIDTH, ID_WIDTH> &ptr) : awid(*(ptr.awid)),
|
||||
awaddr(*(ptr.awaddr)),
|
||||
awlen(*(ptr.awlen)),
|
||||
awsize(*(ptr.awsize)),
|
||||
awburst(*(ptr.awburst)),
|
||||
awvalid(*(ptr.awvalid)),
|
||||
awready(*(ptr.awready)),
|
||||
wdata(*(ptr.wdata)),
|
||||
wstrb(*(ptr.wstrb)),
|
||||
wlast(*(ptr.wlast)),
|
||||
wvalid(*(ptr.wvalid)),
|
||||
wready(*(ptr.wready)),
|
||||
bid(*(ptr.bid)),
|
||||
bresp(*(ptr.bresp)),
|
||||
bvalid(*(ptr.bvalid)),
|
||||
bready(*(ptr.bready)),
|
||||
arid(*(ptr.arid)),
|
||||
araddr(*(ptr.araddr)),
|
||||
arlen(*(ptr.arlen)),
|
||||
arsize(*(ptr.arsize)),
|
||||
arburst(*(ptr.arburst)),
|
||||
arvalid(*(ptr.arvalid)),
|
||||
arready(*(ptr.arready)),
|
||||
rid(*(ptr.rid)),
|
||||
rdata(*(ptr.rdata)),
|
||||
rresp(*(ptr.rresp)),
|
||||
rlast(*(ptr.rlast)),
|
||||
rvalid(*(ptr.rvalid)),
|
||||
rready(*(ptr.rready))
|
||||
{
|
||||
}
|
||||
|
||||
axi4_ref(axi4<A_WIDTH, D_WIDTH, ID_WIDTH> &axi4);
|
||||
};
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
struct axi4
|
||||
{
|
||||
AUTO_IN(awid, ID_WIDTH - 1, 0);
|
||||
AUTO_IN(awaddr, A_WIDTH - 1, 0);
|
||||
AUTO_IN(awlen, 7, 0);
|
||||
AUTO_IN(awsize, 2, 0);
|
||||
AUTO_IN(awburst, 1, 0);
|
||||
AUTO_IN(awvalid, 0, 0);
|
||||
AUTO_OUT(awready, 0, 0);
|
||||
// w
|
||||
AUTO_IN(wdata, D_WIDTH - 1, 0);
|
||||
AUTO_IN(wstrb, (D_WIDTH / 8) - 1, 0);
|
||||
AUTO_IN(wlast, 0, 0);
|
||||
AUTO_IN(wvalid, 0, 0);
|
||||
AUTO_OUT(wready, 0, 0);
|
||||
// b
|
||||
AUTO_OUT(bid, ID_WIDTH - 1, 0);
|
||||
AUTO_OUT(bresp, 1, 0);
|
||||
AUTO_OUT(bvalid, 0, 0);
|
||||
AUTO_IN(bready, 0, 0);
|
||||
// ar
|
||||
AUTO_IN(arid, ID_WIDTH - 1, 0);
|
||||
AUTO_IN(araddr, A_WIDTH - 1, 0);
|
||||
AUTO_IN(arlen, 7, 0);
|
||||
AUTO_IN(arsize, 2, 0);
|
||||
AUTO_IN(arburst, 1, 0);
|
||||
AUTO_IN(arvalid, 0, 0);
|
||||
AUTO_OUT(arready, 0, 0);
|
||||
// r
|
||||
AUTO_OUT(rid, ID_WIDTH - 1, 0);
|
||||
AUTO_OUT(rdata, D_WIDTH - 1, 0);
|
||||
AUTO_OUT(rresp, 1, 0);
|
||||
AUTO_OUT(rlast, 0, 0);
|
||||
AUTO_OUT(rvalid, 0, 0);
|
||||
AUTO_IN(rready, 0, 0);
|
||||
axi4()
|
||||
{
|
||||
// reset all pointer to zero
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
void update_input(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &ref)
|
||||
{
|
||||
// aw
|
||||
awid = ref.awid;
|
||||
awaddr = ref.awaddr;
|
||||
awlen = ref.awlen;
|
||||
awsize = ref.awsize;
|
||||
awburst = ref.awburst;
|
||||
awvalid = ref.awvalid;
|
||||
// w
|
||||
wdata = ref.wdata;
|
||||
wstrb = ref.wstrb;
|
||||
wlast = ref.wlast;
|
||||
wvalid = ref.wvalid;
|
||||
// b
|
||||
bready = ref.bready;
|
||||
// arid
|
||||
arid = ref.arid;
|
||||
araddr = ref.araddr;
|
||||
arlen = ref.arlen;
|
||||
arsize = ref.arsize;
|
||||
arburst = ref.arburst;
|
||||
arvalid = ref.arvalid;
|
||||
// r
|
||||
rready = ref.rready;
|
||||
}
|
||||
void update_output(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &ref)
|
||||
{
|
||||
ref.awready = awready;
|
||||
ref.wready = wready;
|
||||
ref.bid = bid;
|
||||
ref.bresp = bresp;
|
||||
ref.bvalid = bvalid;
|
||||
ref.arready = arready;
|
||||
ref.rid = rid;
|
||||
ref.rdata = rdata;
|
||||
ref.rresp = rresp;
|
||||
ref.rlast = rlast;
|
||||
ref.rvalid = rvalid;
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned int A_WIDTH, unsigned int D_WIDTH, unsigned int ID_WIDTH>
|
||||
axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH>::axi4_ref(axi4<A_WIDTH, D_WIDTH, ID_WIDTH> &axi4) : awid(axi4.awid),
|
||||
awaddr(axi4.awaddr),
|
||||
awlen(axi4.awlen),
|
||||
awsize(axi4.awsize),
|
||||
awburst(axi4.awburst),
|
||||
awvalid(axi4.awvalid),
|
||||
awready(axi4.awready),
|
||||
wdata(axi4.wdata),
|
||||
wstrb(axi4.wstrb),
|
||||
wlast(axi4.wlast),
|
||||
wvalid(axi4.wvalid),
|
||||
wready(axi4.wready),
|
||||
bid(axi4.bid),
|
||||
bresp(axi4.bresp),
|
||||
bvalid(axi4.bvalid),
|
||||
bready(axi4.bready),
|
||||
arid(axi4.arid),
|
||||
araddr(axi4.araddr),
|
||||
arlen(axi4.arlen),
|
||||
arsize(axi4.arsize),
|
||||
arburst(axi4.arburst),
|
||||
arvalid(axi4.arvalid),
|
||||
arready(axi4.arready),
|
||||
rid(axi4.rid),
|
||||
rdata(axi4.rdata),
|
||||
rresp(axi4.rresp),
|
||||
rlast(axi4.rlast),
|
||||
rvalid(axi4.rvalid),
|
||||
rready(axi4.rready)
|
||||
{
|
||||
}
|
||||
|
||||
enum axi_resp
|
||||
{
|
||||
RESP_OKEY = 0,
|
||||
RESP_EXOKEY = 1,
|
||||
RESP_SLVERR = 2,
|
||||
RESP_DECERR = 3
|
||||
};
|
||||
|
||||
enum axi_burst_type
|
||||
{
|
||||
BURST_FIXED = 0,
|
||||
BURST_INCR = 1,
|
||||
BURST_WRAP = 2,
|
||||
BURST_RESERVED = 3
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,88 +0,0 @@
|
||||
#ifndef AXI4_MEM
|
||||
#define AXI4_MEM
|
||||
|
||||
#include "axi4_slave.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
class axi4_mem : public axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>
|
||||
{
|
||||
public:
|
||||
axi4_mem(size_t size_bytes)
|
||||
{
|
||||
if (size_bytes % (D_WIDTH / 8))
|
||||
size_bytes += 8 - (size_bytes % (D_WIDTH / 8));
|
||||
mem = new unsigned char[size_bytes];
|
||||
mem_size = size_bytes;
|
||||
}
|
||||
axi4_mem(size_t size_bytes, const uint8_t *init_binary, size_t init_binary_len) : axi4_mem(size_bytes)
|
||||
{
|
||||
assert(init_binary_len <= size_bytes, "init_binary_len is larger than memory size.");
|
||||
memcpy(mem, init_binary, init_binary_len);
|
||||
}
|
||||
~axi4_mem()
|
||||
{
|
||||
delete[] mem;
|
||||
}
|
||||
bool read(off_t start_addr, size_t size, uint8_t *buffer)
|
||||
{
|
||||
if (start_addr + size <= mem_size)
|
||||
{
|
||||
memcpy(buffer, &mem[start_addr], size);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
bool write(off_t start_addr, size_t size, const uint8_t *buffer)
|
||||
{
|
||||
if (start_addr + size <= mem_size)
|
||||
{
|
||||
memcpy(&mem[start_addr], buffer, size);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void load_binary(const char *init_file, uint64_t start_addr = 0)
|
||||
{
|
||||
std::ifstream file(init_file, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
size_t file_size = file.tellg();
|
||||
file.seekg(std::ios_base::beg);
|
||||
if (start_addr >= mem_size || file_size > mem_size - start_addr)
|
||||
{
|
||||
std::cerr << "memory size is not big enough for init file." << std::endl;
|
||||
file_size = mem_size;
|
||||
}
|
||||
file.read((char *)mem + start_addr, file_size);
|
||||
}
|
||||
|
||||
protected:
|
||||
axi_resp do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer)
|
||||
{
|
||||
if (start_addr + size <= mem_size)
|
||||
{
|
||||
memcpy(buffer, &mem[start_addr], size);
|
||||
return RESP_OKEY;
|
||||
}
|
||||
else
|
||||
return RESP_DECERR;
|
||||
}
|
||||
axi_resp do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer)
|
||||
{
|
||||
if (start_addr + size <= mem_size)
|
||||
{
|
||||
memcpy(&mem[start_addr], buffer, size);
|
||||
return RESP_OKEY;
|
||||
}
|
||||
else
|
||||
return RESP_DECERR;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *mem;
|
||||
size_t mem_size;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,340 +0,0 @@
|
||||
#ifndef AXI4_SLAVE
|
||||
#define AXI4_SLAVE
|
||||
|
||||
#include "axi4.hpp"
|
||||
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
class axi4_slave
|
||||
{
|
||||
static_assert(D_WIDTH <= 64, "D_WIDTH should be <= 64.");
|
||||
static_assert(A_WIDTH <= 64, "A_WIDTH should be <= 64.");
|
||||
|
||||
public:
|
||||
axi4_slave(int delay = 0) : delay(delay)
|
||||
{
|
||||
}
|
||||
void beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
read_channel(pin);
|
||||
write_channel(pin);
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
read_busy = false;
|
||||
read_last = false;
|
||||
read_wait = false;
|
||||
read_delay = 0;
|
||||
write_busy = false;
|
||||
b_busy = false;
|
||||
write_delay = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual axi_resp do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
|
||||
virtual axi_resp do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
|
||||
|
||||
private:
|
||||
unsigned int D_bytes = D_WIDTH / 8;
|
||||
int delay;
|
||||
|
||||
private:
|
||||
bool read_busy = false; // during trascation except last
|
||||
bool read_last = false; // wait rready and free
|
||||
bool read_wait = false; // ar ready, but waiting the last read to ready
|
||||
int read_delay = 0; // delay
|
||||
uint64_t r_start_addr; // lower bound of transaction address
|
||||
uint64_t r_current_addr; // current burst address in r_data buffer (physical address % 4096)
|
||||
AUTO_SIG(arid, ID_WIDTH - 1, 0);
|
||||
axi_burst_type r_burst_type;
|
||||
unsigned int r_each_len;
|
||||
unsigned int r_nr_trans;
|
||||
unsigned int r_cur_trans;
|
||||
unsigned int r_tot_len;
|
||||
bool r_out_ready;
|
||||
bool r_early_err;
|
||||
axi_resp r_resp;
|
||||
uint8_t r_data[4096];
|
||||
|
||||
bool read_check()
|
||||
{
|
||||
if (r_burst_type == BURST_RESERVED)
|
||||
return false;
|
||||
if (r_burst_type == BURST_WRAP && (r_current_addr % r_each_len))
|
||||
return false;
|
||||
if (r_burst_type == BURST_WRAP)
|
||||
{
|
||||
if (r_nr_trans != 2 || r_nr_trans != 4 || r_nr_trans != 8 || r_nr_trans != 16)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint64_t rem_addr = 4096 - (r_start_addr % 4096);
|
||||
if (r_tot_len > rem_addr)
|
||||
{
|
||||
printf("r_tot_len: %d, rem_addr: %ld\n", r_tot_len, rem_addr);
|
||||
return false;
|
||||
}
|
||||
if (r_each_len > D_bytes)
|
||||
{
|
||||
printf("r_each_len: %d, D_bytes: %d\n", r_each_len, D_bytes);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void read_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
pin.rid = arid;
|
||||
pin.rvalid = 1;
|
||||
bool update = false;
|
||||
if (pin.rready || r_cur_trans == 0)
|
||||
{
|
||||
r_cur_trans += 1;
|
||||
update = true;
|
||||
if (r_cur_trans == r_nr_trans)
|
||||
{
|
||||
read_last = true;
|
||||
read_busy = false;
|
||||
}
|
||||
}
|
||||
pin.rlast = read_last;
|
||||
if (update)
|
||||
{
|
||||
if (r_early_err)
|
||||
{
|
||||
pin.rresp = RESP_DECERR;
|
||||
pin.rdata = 0;
|
||||
}
|
||||
else if (r_burst_type == BURST_FIXED)
|
||||
{
|
||||
pin.rresp = do_read(static_cast<uint64_t>(r_start_addr), static_cast<uint64_t>(r_tot_len), &r_data[r_start_addr % 4096]);
|
||||
pin.rdata = *(AUTO_SIG(*, D_WIDTH - 1, 0))(&r_data[(r_start_addr % 4096) - (r_start_addr % D_bytes)]);
|
||||
}
|
||||
else
|
||||
{ // INCR, WRAP
|
||||
pin.rresp = r_resp;
|
||||
pin.rdata = *(AUTO_SIG(*, D_WIDTH - 1, 0))(&r_data[r_current_addr - (r_current_addr % D_bytes)]);
|
||||
r_current_addr += r_each_len - (r_current_addr % r_each_len);
|
||||
if (r_burst_type == BURST_WRAP && r_current_addr == ((r_start_addr % 4096) + r_each_len * r_nr_trans))
|
||||
{
|
||||
r_current_addr = r_start_addr % 4096;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_init(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
arid = static_cast<unsigned int>(pin.arid);
|
||||
r_burst_type = static_cast<axi_burst_type>(pin.arburst);
|
||||
r_each_len = 1 << pin.arsize;
|
||||
r_nr_trans = pin.arlen + 1;
|
||||
r_current_addr = (r_burst_type == BURST_WRAP) ? (pin.araddr % 4096) : ((pin.araddr % 4096) - (pin.araddr % r_each_len));
|
||||
r_start_addr = (r_burst_type == BURST_WRAP) ? (pin.araddr - (pin.araddr % (r_each_len * r_nr_trans))) : pin.araddr;
|
||||
r_cur_trans = 0;
|
||||
r_tot_len = ((r_burst_type == BURST_FIXED) ? r_each_len : r_each_len * r_nr_trans) - (r_start_addr % r_each_len); // first beat can be unaligned
|
||||
r_early_err = !read_check();
|
||||
assert(!r_early_err, "r early error");
|
||||
// clear unused bits.
|
||||
if (r_start_addr % D_bytes)
|
||||
{
|
||||
uint64_t clear_addr = r_start_addr % 4096;
|
||||
clear_addr -= clear_addr % D_bytes;
|
||||
memset(&r_data[clear_addr], 0x00, D_bytes);
|
||||
}
|
||||
if ((r_start_addr + r_tot_len) % D_bytes)
|
||||
{
|
||||
uint64_t clear_addr = (r_start_addr + r_tot_len) % 4096;
|
||||
clear_addr -= (clear_addr % D_bytes);
|
||||
memset(&r_data[clear_addr], 0x00, D_bytes);
|
||||
}
|
||||
// For BURST_FIXED, we call do_read every read burst
|
||||
if (!r_early_err && r_burst_type != BURST_FIXED)
|
||||
r_resp = do_read(static_cast<uint64_t>(r_start_addr), static_cast<uint64_t>(r_tot_len), &r_data[r_start_addr % 4096]);
|
||||
}
|
||||
|
||||
void read_channel(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
// Read step 1. release old transaction
|
||||
if (read_last && pin.rready)
|
||||
{
|
||||
read_last = false;
|
||||
pin.rvalid = 0; // maybe change in the following code
|
||||
pin.rlast = 0;
|
||||
if (read_wait)
|
||||
{
|
||||
read_wait = false;
|
||||
read_busy = true;
|
||||
read_delay = delay;
|
||||
}
|
||||
}
|
||||
// Read step 2. check new address come
|
||||
if (pin.arready && pin.arvalid)
|
||||
{
|
||||
read_init(pin);
|
||||
if (read_last)
|
||||
read_wait = true;
|
||||
else
|
||||
{
|
||||
read_busy = true;
|
||||
read_delay = delay;
|
||||
}
|
||||
}
|
||||
// Read step 3. do read trascation
|
||||
if (read_busy)
|
||||
{
|
||||
if (read_delay)
|
||||
read_delay--;
|
||||
else
|
||||
read_beat(pin);
|
||||
}
|
||||
// Read step 4. set arready before new address come, it will change read_busy and read_wait status
|
||||
pin.arready = !read_busy && !read_wait;
|
||||
}
|
||||
|
||||
private:
|
||||
bool write_busy = false;
|
||||
bool b_busy = false;
|
||||
int write_delay = 0;
|
||||
uint64_t w_start_addr;
|
||||
uint64_t w_current_addr;
|
||||
AUTO_SIG(awid, ID_WIDTH - 1, 0);
|
||||
axi_burst_type w_burst_type;
|
||||
unsigned int w_each_len;
|
||||
int w_nr_trans;
|
||||
int w_cur_trans;
|
||||
unsigned int w_tot_len;
|
||||
bool w_out_ready;
|
||||
bool w_early_err;
|
||||
axi_resp w_resp;
|
||||
uint8_t w_buffer[D_WIDTH / 8];
|
||||
bool write_check()
|
||||
{
|
||||
if (w_burst_type == BURST_RESERVED)
|
||||
return false; // TODO:去掉了或,不知道对不对
|
||||
// if (w_burst_type == BURST_RESERVED || w_burst_type) return false;
|
||||
if (w_burst_type == BURST_WRAP && (w_current_addr % w_each_len))
|
||||
return false;
|
||||
if (w_burst_type == BURST_WRAP)
|
||||
{
|
||||
if (w_nr_trans != 2 || w_nr_trans != 4 || w_nr_trans != 8 || w_nr_trans != 16)
|
||||
return false;
|
||||
}
|
||||
uint64_t rem_addr = 4096 - (w_start_addr % 4096);
|
||||
if (w_tot_len > rem_addr)
|
||||
return false;
|
||||
if (w_each_len > D_bytes)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
void write_init(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
awid = pin.awid;
|
||||
w_burst_type = static_cast<axi_burst_type>(pin.awburst);
|
||||
w_each_len = 1 << pin.awsize;
|
||||
w_nr_trans = pin.awlen + 1;
|
||||
w_current_addr = (w_burst_type == BURST_WRAP) ? pin.awaddr : (pin.awaddr - (pin.awaddr % w_each_len));
|
||||
w_start_addr = (w_burst_type == BURST_WRAP) ? (pin.awaddr - (pin.awaddr % (w_each_len * w_nr_trans))) : pin.awaddr;
|
||||
w_cur_trans = 0;
|
||||
w_tot_len = w_each_len * w_nr_trans - (w_start_addr % w_each_len);
|
||||
w_early_err = !write_check();
|
||||
assert(!w_early_err, "w early error");
|
||||
w_resp = RESP_OKEY;
|
||||
}
|
||||
// pair<start,len>
|
||||
std::vector<std::pair<int, int>> strb_to_range(AUTO_IN(wstrb, (D_WIDTH / 8) - 1, 0), int st_pos, int ed_pos)
|
||||
{
|
||||
std::vector<std::pair<int, int>> res;
|
||||
int l = st_pos;
|
||||
while (l < ed_pos)
|
||||
{
|
||||
if ((wstrb >> l) & 1)
|
||||
{
|
||||
int r = l;
|
||||
while ((wstrb >> r) & 1 && r < ed_pos)
|
||||
r++;
|
||||
res.emplace_back(l, r - l);
|
||||
l = r + 1;
|
||||
}
|
||||
else
|
||||
l++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void write_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
if (pin.wvalid && pin.wready)
|
||||
{
|
||||
w_cur_trans += 1;
|
||||
if (w_cur_trans == w_nr_trans)
|
||||
{
|
||||
write_busy = false;
|
||||
b_busy = true;
|
||||
if (!pin.wlast)
|
||||
{
|
||||
w_early_err = true;
|
||||
assert(false, "w last error");
|
||||
}
|
||||
}
|
||||
uint64_t addr_base = w_current_addr;
|
||||
if (w_burst_type != BURST_FIXED)
|
||||
{
|
||||
w_current_addr += w_each_len - (addr_base % w_each_len);
|
||||
if (w_current_addr == (w_start_addr + w_each_len * w_nr_trans))
|
||||
w_cur_trans = w_start_addr; // warp support
|
||||
}
|
||||
uint64_t in_data_pos = addr_base % D_bytes;
|
||||
addr_base -= addr_base % D_bytes;
|
||||
uint64_t rem_data_pos = w_each_len - (in_data_pos % w_each_len); // unaligned support
|
||||
// split discontinuous wstrb bits to small requests
|
||||
std::vector<std::pair<int, int>> range = strb_to_range(pin.wstrb, in_data_pos, in_data_pos + rem_data_pos);
|
||||
for (std::pair<int, int> sub_range : range)
|
||||
{
|
||||
int &addr = sub_range.first;
|
||||
int &len = sub_range.second;
|
||||
memcpy(w_buffer, &(pin.wdata), sizeof(pin.wdata));
|
||||
w_resp = static_cast<axi_resp>(static_cast<int>(w_resp) | static_cast<int>(do_write(addr_base + addr, len, w_buffer + addr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
void b_beat(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
pin.bid = awid;
|
||||
pin.bresp = w_early_err ? RESP_DECERR : w_resp;
|
||||
if (pin.bvalid && pin.bready)
|
||||
b_busy = false;
|
||||
}
|
||||
void write_channel(axi4_ref<A_WIDTH, D_WIDTH, ID_WIDTH> &pin)
|
||||
{
|
||||
if (pin.awready && pin.awvalid)
|
||||
{
|
||||
write_init(pin);
|
||||
write_busy = true;
|
||||
write_delay = delay;
|
||||
}
|
||||
if (write_busy)
|
||||
{
|
||||
if (write_delay)
|
||||
write_delay--;
|
||||
else
|
||||
write_beat(pin);
|
||||
}
|
||||
if (b_busy)
|
||||
{
|
||||
b_beat(pin);
|
||||
}
|
||||
pin.bvalid = b_busy;
|
||||
pin.awready = !write_busy && !b_busy;
|
||||
if (delay)
|
||||
pin.wready = write_busy && !write_delay;
|
||||
else
|
||||
pin.wready = !b_busy;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,88 +0,0 @@
|
||||
#ifndef AXI4_XBAR_H
|
||||
#define AXI4_XBAR_H
|
||||
|
||||
#include "axi4.hpp"
|
||||
#include "axi4_slave.hpp"
|
||||
#include "mmio_dev.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
extern long long current_pc;
|
||||
|
||||
template <unsigned int A_WIDTH = 64, unsigned int D_WIDTH = 64, unsigned int ID_WIDTH = 4>
|
||||
class axi4_xbar : public axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>
|
||||
{
|
||||
public:
|
||||
axi4_xbar(int delay = 0) : axi4_slave<A_WIDTH, D_WIDTH, ID_WIDTH>(delay)
|
||||
{
|
||||
}
|
||||
bool add_dev(uint64_t start_addr, uint64_t length, mmio_dev *dev)
|
||||
{
|
||||
std::pair<uint64_t, uint64_t> addr_range = std::make_pair(start_addr, start_addr + length);
|
||||
if (start_addr % length)
|
||||
{
|
||||
printf("mmio device start address must be aligned to its length\n");
|
||||
return false;
|
||||
}
|
||||
// check range
|
||||
auto it = devices.upper_bound(addr_range);
|
||||
if (it != devices.end())
|
||||
{
|
||||
uint64_t l_max = std::max(it->first.first, addr_range.first);
|
||||
uint64_t r_min = std::min(it->first.second, addr_range.second);
|
||||
if (l_max < r_min)
|
||||
return false; // overleap
|
||||
}
|
||||
if (it != devices.begin())
|
||||
{
|
||||
it = std::prev(it);
|
||||
uint64_t l_max = std::max(it->first.first, addr_range.first);
|
||||
uint64_t r_min = std::min(it->first.second, addr_range.second);
|
||||
if (l_max < r_min)
|
||||
return false; // overleap
|
||||
}
|
||||
// overleap check pass
|
||||
devices[addr_range] = dev;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
axi_resp do_read(uint64_t start_addr, uint64_t size, unsigned char *buffer)
|
||||
{
|
||||
auto it = devices.upper_bound(std::make_pair(start_addr, ULONG_MAX));
|
||||
if (it == devices.begin())
|
||||
return RESP_DECERR;
|
||||
it = std::prev(it);
|
||||
uint64_t end_addr = start_addr + size;
|
||||
if (it->first.first <= start_addr && end_addr <= it->first.second)
|
||||
{
|
||||
uint64_t dev_size = it->first.second - it->first.first;
|
||||
return it->second->do_read(start_addr % dev_size, size, buffer) ? RESP_OKEY : RESP_SLVERR;
|
||||
}
|
||||
else
|
||||
return RESP_DECERR;
|
||||
}
|
||||
axi_resp do_write(uint64_t start_addr, uint64_t size, const unsigned char *buffer)
|
||||
{
|
||||
auto it = devices.upper_bound(std::make_pair(start_addr, ULONG_MAX));
|
||||
if (it == devices.begin())
|
||||
return RESP_DECERR;
|
||||
it = std::prev(it);
|
||||
uint64_t end_addr = start_addr + size;
|
||||
if (it->first.first <= start_addr && end_addr <= it->first.second)
|
||||
{
|
||||
uint64_t dev_size = it->first.second - it->first.first;
|
||||
return it->second->do_write(start_addr % dev_size, size, buffer) ? RESP_OKEY : RESP_SLVERR;
|
||||
}
|
||||
else
|
||||
return RESP_DECERR;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::pair<uint64_t, uint64_t>, mmio_dev *> devices;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,11 +1,10 @@
|
||||
#ifndef MMIO_DEV_H
|
||||
#define MMIO_DEV_H
|
||||
|
||||
class mmio_dev
|
||||
{
|
||||
class mmio_dev {
|
||||
public:
|
||||
virtual bool do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
|
||||
virtual bool do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
|
||||
virtual bool do_read (uint64_t start_addr, uint64_t size, uint8_t* buffer) = 0;
|
||||
virtual bool do_write(uint64_t start_addr, uint64_t size, const uint8_t* buffer) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,123 @@
|
||||
#ifndef NSCSCC_SRAM_HPP
|
||||
#define NSCSCC_SRAM_HPP
|
||||
|
||||
#include <verilated.h>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
#define AUTO_SIG(name, msb, lsb) \
|
||||
typename std::conditional <(msb-lsb+1) <= 8, CData, \
|
||||
typename std::conditional <(msb-lsb+1) <= 16, SData, \
|
||||
typename std::conditional <(msb-lsb+1) <= 32, IData, QData >::type >::type >::type name
|
||||
|
||||
#define AUTO_IN(name, msb, lsb) AUTO_SIG(name, msb, lsb)
|
||||
#define AUTO_OUT(name, msb, lsb) AUTO_SIG(name, msb, lsb)
|
||||
|
||||
struct nscscc_sram;
|
||||
|
||||
struct nscscc_sram_ptr {
|
||||
// inst channel
|
||||
AUTO_IN (*inst_sram_en, 0, 0) = NULL;
|
||||
AUTO_IN (*inst_sram_wen, 3, 0) = NULL;
|
||||
AUTO_IN (*inst_sram_addr, 31, 0) = NULL;
|
||||
AUTO_IN (*inst_sram_wdata, 31, 0) = NULL;
|
||||
AUTO_OUT(*inst_sram_rdata, 31, 0) = NULL;
|
||||
// data channel
|
||||
AUTO_IN (*data_sram_en, 0, 0) = NULL;
|
||||
AUTO_IN (*data_sram_wen, 7, 0) = NULL;
|
||||
AUTO_IN (*data_sram_addr, 31, 0) = NULL;
|
||||
AUTO_IN (*data_sram_wdata, 63, 0) = NULL;
|
||||
AUTO_OUT(*data_sram_rdata, 63, 0) = NULL;
|
||||
bool check() {
|
||||
std::set <void*> s;
|
||||
s.insert((void*)inst_sram_en);
|
||||
s.insert((void*)inst_sram_wen);
|
||||
s.insert((void*)inst_sram_addr);
|
||||
s.insert((void*)inst_sram_wdata);
|
||||
s.insert((void*)inst_sram_rdata);
|
||||
s.insert((void*)data_sram_en);
|
||||
s.insert((void*)data_sram_wen);
|
||||
s.insert((void*)data_sram_addr);
|
||||
s.insert((void*)data_sram_wdata);
|
||||
s.insert((void*)data_sram_rdata);
|
||||
return s.size() == 10 && s.count(NULL) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct nscscc_sram_ref {
|
||||
// inst channel
|
||||
AUTO_IN (&inst_sram_en, 0, 0);
|
||||
AUTO_IN (&inst_sram_wen, 3, 0);
|
||||
AUTO_IN (&inst_sram_addr, 31, 0);
|
||||
AUTO_IN (&inst_sram_wdata, 31, 0);
|
||||
AUTO_OUT(&inst_sram_rdata, 31, 0);
|
||||
// data channel
|
||||
AUTO_IN (&data_sram_en, 0, 0);
|
||||
AUTO_IN (&data_sram_wen, 7, 0);
|
||||
AUTO_IN (&data_sram_addr, 31, 0);
|
||||
AUTO_IN (&data_sram_wdata, 63, 0);
|
||||
AUTO_OUT(&data_sram_rdata, 63, 0);
|
||||
nscscc_sram_ref(nscscc_sram_ptr &ptr):
|
||||
inst_sram_en (*(ptr.inst_sram_en)),
|
||||
inst_sram_wen (*(ptr.inst_sram_wen)),
|
||||
inst_sram_addr (*(ptr.inst_sram_addr)),
|
||||
inst_sram_wdata (*(ptr.inst_sram_wdata)),
|
||||
inst_sram_rdata (*(ptr.inst_sram_rdata)),
|
||||
data_sram_en (*(ptr.data_sram_en)),
|
||||
data_sram_wen (*(ptr.data_sram_wen)),
|
||||
data_sram_addr (*(ptr.data_sram_addr)),
|
||||
data_sram_wdata (*(ptr.data_sram_wdata)),
|
||||
data_sram_rdata (*(ptr.data_sram_rdata))
|
||||
{}
|
||||
nscscc_sram_ref(nscscc_sram &sram);
|
||||
};
|
||||
|
||||
struct nscscc_sram {
|
||||
// inst channel
|
||||
AUTO_IN (inst_sram_en, 0, 0);
|
||||
AUTO_IN (inst_sram_wen, 3, 0);
|
||||
AUTO_IN (inst_sram_addr, 31, 0);
|
||||
AUTO_IN (inst_sram_wdata, 31, 0);
|
||||
AUTO_OUT(inst_sram_rdata, 31, 0);
|
||||
// data channel
|
||||
AUTO_IN (data_sram_en, 0, 0);
|
||||
AUTO_IN (data_sram_wen, 7, 0);
|
||||
AUTO_IN (data_sram_addr, 31, 0);
|
||||
AUTO_IN (data_sram_wdata, 63, 0);
|
||||
AUTO_OUT(data_sram_rdata, 63, 0);
|
||||
nscscc_sram() {
|
||||
// reset all pointer to zero
|
||||
memset(this,0,sizeof(*this));
|
||||
}
|
||||
void update_input(nscscc_sram_ref &ref) {
|
||||
inst_sram_en = ref.inst_sram_en;
|
||||
inst_sram_wen = ref.inst_sram_wen;
|
||||
inst_sram_addr = ref.inst_sram_addr;
|
||||
inst_sram_wdata = ref.inst_sram_wdata;
|
||||
data_sram_en = ref.data_sram_en;
|
||||
data_sram_wen = ref.data_sram_wen;
|
||||
data_sram_addr = ref.data_sram_addr;
|
||||
data_sram_wdata = ref.data_sram_wdata;
|
||||
}
|
||||
void update_output(nscscc_sram_ref &ref) {
|
||||
ref.inst_sram_rdata = inst_sram_rdata;
|
||||
ref.data_sram_rdata = data_sram_rdata;
|
||||
}
|
||||
};
|
||||
|
||||
nscscc_sram_ref::nscscc_sram_ref(nscscc_sram &sram):
|
||||
inst_sram_en (sram.inst_sram_en),
|
||||
inst_sram_wen (sram.inst_sram_wen),
|
||||
inst_sram_addr (sram.inst_sram_addr),
|
||||
inst_sram_wdata (sram.inst_sram_wdata),
|
||||
inst_sram_rdata (sram.inst_sram_rdata),
|
||||
data_sram_en (sram.data_sram_en),
|
||||
data_sram_wen (sram.data_sram_wen),
|
||||
data_sram_addr (sram.data_sram_addr),
|
||||
data_sram_wdata (sram.data_sram_wdata),
|
||||
data_sram_rdata (sram.data_sram_rdata)
|
||||
{}
|
||||
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,76 @@
|
||||
#ifndef NSCSCC_SRAM_SLAVE
|
||||
#define NSCSCC_SRAM_SLAVE
|
||||
|
||||
#include "nscscc_sram.hpp"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class nscscc_sram_slave
|
||||
{
|
||||
public:
|
||||
nscscc_sram_slave()
|
||||
{
|
||||
}
|
||||
void beat(nscscc_sram_ref &pin)
|
||||
{
|
||||
inst_channel(pin);
|
||||
data_channel(pin);
|
||||
}
|
||||
void inst_channel(nscscc_sram_ref &pin)
|
||||
{
|
||||
if (pin.inst_sram_en)
|
||||
{
|
||||
assert(!pin.inst_sram_wen);
|
||||
do_read(pin.inst_sram_addr & address_mask, 4, (uint8_t *)&(pin.inst_sram_rdata));
|
||||
}
|
||||
}
|
||||
void data_channel(nscscc_sram_ref &pin)
|
||||
{
|
||||
if (pin.data_sram_en)
|
||||
{
|
||||
if (pin.data_sram_wen)
|
||||
{
|
||||
std::vector<std::pair<int, int>> write_range = strb_to_range(pin.data_sram_wen);
|
||||
for (std::pair<int, int> sub_range : write_range)
|
||||
{
|
||||
int &addr = sub_range.first;
|
||||
int &len = sub_range.second;
|
||||
do_write(((pin.data_sram_addr & address_mask) & 0xfffffff8) + addr, len, ((uint8_t *)&(pin.data_sram_wdata)) + addr);
|
||||
}
|
||||
}
|
||||
do_read(pin.data_sram_addr & address_mask, 8, (uint8_t *)&(pin.data_sram_rdata));
|
||||
}
|
||||
}
|
||||
void set_address_mask(uint32_t new_mask)
|
||||
{
|
||||
address_mask = new_mask;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void do_read(uint64_t start_addr, uint64_t size, uint8_t *buffer) = 0;
|
||||
virtual void do_write(uint64_t start_addr, uint64_t size, const uint8_t *buffer) = 0;
|
||||
|
||||
private:
|
||||
uint32_t address_mask = 0xffffffff;
|
||||
std::vector<std::pair<int, int>> strb_to_range(AUTO_IN(wen, 7, 0))
|
||||
{
|
||||
std::vector<std::pair<int, int>> res;
|
||||
int l = 0;
|
||||
while (l < 8)
|
||||
{
|
||||
if ((wen >> l) & 1)
|
||||
{
|
||||
int r = l;
|
||||
while ((wen >> r) & 1 && r < 8)
|
||||
r++;
|
||||
res.emplace_back(l, r - l);
|
||||
l = r + 1;
|
||||
}
|
||||
else
|
||||
l++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,94 @@
|
||||
#ifndef NSCSCC_SRAM_XBAR
|
||||
#define NSCSCC_SRAM_XBAR
|
||||
|
||||
#include "nscscc_sram_slave.hpp"
|
||||
#include "mmio_dev.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
void debug()
|
||||
{
|
||||
}
|
||||
|
||||
class nscscc_sram_xbar : public nscscc_sram_slave
|
||||
{
|
||||
public:
|
||||
nscscc_sram_xbar()
|
||||
{
|
||||
}
|
||||
bool add_dev(uint64_t start_addr, uint64_t length, mmio_dev *dev, bool byte_mode = false)
|
||||
{
|
||||
std::tuple<uint64_t, uint64_t, bool> addr_range = std::make_tuple(start_addr, start_addr + length, byte_mode);
|
||||
if (start_addr % length)
|
||||
return false;
|
||||
// check range
|
||||
auto it = devices.upper_bound(addr_range);
|
||||
if (it != devices.end())
|
||||
{
|
||||
uint64_t l_max = std::max(std::get<0>(it->first), std::get<0>(addr_range));
|
||||
uint64_t r_min = std::min(std::get<1>(it->first), std::get<1>(addr_range));
|
||||
if (l_max < r_min)
|
||||
return false; // overleap
|
||||
}
|
||||
if (it != devices.begin())
|
||||
{
|
||||
it = std::prev(it);
|
||||
uint64_t l_max = std::max(std::get<0>(it->first), std::get<0>(addr_range));
|
||||
uint64_t r_min = std::min(std::get<1>(it->first), std::get<1>(addr_range));
|
||||
if (l_max < r_min)
|
||||
return false; // overleap
|
||||
}
|
||||
// overleap check pass
|
||||
devices[addr_range] = dev;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void do_read(uint64_t start_addr, uint64_t size, unsigned char *buffer)
|
||||
{
|
||||
auto it = devices.upper_bound(std::make_tuple(start_addr, ULONG_MAX, true));
|
||||
if (it == devices.begin())
|
||||
{
|
||||
*((uint32_t *)buffer) = 0xdec0dee3;
|
||||
return;
|
||||
}
|
||||
it = std::prev(it);
|
||||
if (std::get<0>(it->first) <= start_addr && start_addr + 1 <= std::get<1>(it->first))
|
||||
{
|
||||
uint64_t dev_size = std::get<1>(it->first) - std::get<0>(it->first);
|
||||
if (std::get<2>(it->first))
|
||||
size = 1; // treat as byte device
|
||||
else
|
||||
start_addr -= start_addr & (size - 1);
|
||||
it->second->do_read(start_addr % dev_size, size, buffer + (start_addr % size));
|
||||
}
|
||||
else
|
||||
*((uint32_t *)buffer) = 0xdec0dee3;
|
||||
}
|
||||
void do_write(uint64_t start_addr, uint64_t size, const unsigned char *buffer)
|
||||
{
|
||||
auto it = devices.upper_bound(std::make_tuple(start_addr, ULONG_MAX, true));
|
||||
if (it == devices.begin())
|
||||
{
|
||||
*((uint32_t *)buffer) = 0xdec0dee3;
|
||||
return;
|
||||
}
|
||||
it = std::prev(it);
|
||||
uint64_t end_addr = start_addr + size;
|
||||
if (std::get<0>(it->first) <= start_addr && end_addr <= std::get<1>(it->first))
|
||||
{
|
||||
uint64_t dev_size = std::get<1>(it->first) - std::get<0>(it->first);
|
||||
it->second->do_write(start_addr % dev_size, size, buffer);
|
||||
}
|
||||
else
|
||||
*((uint32_t *)buffer) = 0xdec0dee3;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::tuple<uint64_t, uint64_t, bool>, mmio_dev *> devices;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue