vram/VRAMCore: make simulatable in Bluesim, tidy up

This commit is contained in:
David Anderson 2024-09-08 09:26:59 -07:00
parent 79b54ca86f
commit fb57903021
1 changed files with 56 additions and 42 deletions

View File

@ -1,15 +1,9 @@
package VRAMCore; package VRAMCore;
import Connectable::*;
import GetPut::*; import GetPut::*;
import ClientServer::*; import ClientServer::*;
import DReg::*; import BRAMCore::*;
import BRAM::*;
import Vector::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Real::*; import Real::*;
import Printf::*;
import DelayLine::*; import DelayLine::*;
import ECP5_RAM::*; import ECP5_RAM::*;
@ -18,31 +12,71 @@ export VRAMAddr;
export VRAMData; export VRAMData;
export VRAMRequest(..); export VRAMRequest(..);
export VRAMResponse(..); export VRAMResponse(..);
export VRAMClient(..);
export VRAMServer(..);
export VRAMCore(..); export VRAMCore(..);
export mkVRAMCore; export mkVRAMCore;
typedef Bit#(8) VRAMData; typedef Bit#(8) VRAMData;
// Each byte RAM we build below can address 4096 bytes, which is 12 typedef UInt#(17) VRAMAddr;
// address bits. typedef UInt#(2) ArrayAddr;
typedef UInt#(12) ByteAddr;
typedef UInt#(3) ChipAddr; typedef UInt#(3) ChipAddr;
typedef UInt#(12) ByteAddr;
// ByteRAM is two EBRs glued together to make a whole-byte memory. // ByteRAM is two EBRs glued together to make a whole-byte memory.
typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM; typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM;
typedef struct {
VRAMAddr addr;
Maybe#(VRAMData) data;
} VRAMRequest deriving (Bits, Eq);
typedef struct {
VRAMData data;
} VRAMResponse deriving (Bits, Eq);
module mkNibbleRAM_ECP5(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc);
EBRPortConfig cfg = defaultValue;
cfg.chip_select_addr = chip_addr;
let _ret <- mkEBRCore(cfg, cfg);
return _ret;
endmodule
module mkNibbleRAM_Sim(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc);
BRAM_DUAL_PORT#(ByteAddr, Bit#(4)) ram <- mkBRAMCore2(4096, False);
interface EBRPort portA;
method Action put(UInt#(3) chip_select, Bool write, ByteAddr address, Bit#(4) datain);
if (chip_select == chip_addr)
ram.a.put(write, address, datain);
endmethod
method read = ram.a.read;
endinterface
interface EBRPort portB;
method Action put(UInt#(3) chip_select, Bool write, ByteAddr address, Bit#(4) datain);
if (chip_select == chip_addr)
ram.b.put(write, address, datain);
endmethod
method read = ram.b.read;
endinterface
endmodule
module mkNibbleRAM(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc);
let _ret;
if (genC())
_ret <- mkNibbleRAM_Sim(chip_addr);
else
_ret <- mkNibbleRAM_ECP5(chip_addr);
return _ret;
endmodule
// mkByteRAM glues two ECP5 EBRs together to make a 4096x8b memory // mkByteRAM glues two ECP5 EBRs together to make a 4096x8b memory
// block. Like the underlying ECP5 EBRs, callers must bring their own // block. Like the underlying ECP5 EBRs, callers must bring their own
// flow control to read out responses one cycle after putting a read // flow control to read out responses one cycle after putting a read
// request. // request.
module mkByteRAM(ChipAddr chip_addr, ByteRAM ifc); module mkByteRAM(ChipAddr chip_addr, ByteRAM ifc);
EBRPortConfig cfg = defaultValue; EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) upper <- mkNibbleRAM(chip_addr);
cfg.chip_select_addr = chip_addr; EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) lower <- mkNibbleRAM(chip_addr);
EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) upper <- mkEBRCore(cfg, cfg);
EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) lower <- mkEBRCore(cfg, cfg);
interface EBRPort portA; interface EBRPort portA;
method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in); method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in);
@ -119,25 +153,9 @@ module mkByteRAMArray(Integer num_chips, ByteRAM ifc);
endinterface endinterface
endmodule endmodule
typedef UInt#(2) ArrayAddr;
typedef UInt#(17) VRAMAddr;
typedef struct {
VRAMAddr addr;
Maybe#(VRAMData) data;
} VRAMRequest deriving (Bits, Eq);
typedef struct {
VRAMData data;
} VRAMResponse deriving (Bits, Eq);
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
typedef Client#(VRAMRequest, VRAMResponse) VRAMClient;
interface VRAMCore; interface VRAMCore;
interface VRAMServer portA; interface Server#(VRAMRequest, VRAMResponse) portA;
interface VRAMServer portB; interface Server#(VRAMRequest, VRAMResponse) portB;
endinterface endinterface
// mkVRAMCore creates a dual port VRAM of the specified size, using // mkVRAMCore creates a dual port VRAM of the specified size, using
@ -163,11 +181,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
let num_arrays = ceil(fromInteger(num_byterams) / 8); let num_arrays = ceil(fromInteger(num_byterams) / 8);
function Tuple3#(ArrayAddr, ChipAddr, ByteAddr) split_addr(VRAMAddr a); function Tuple3#(ArrayAddr, ChipAddr, ByteAddr) split_addr(VRAMAddr a);
if (num_bytes < 128*1024) return unpack(pack(a));
a = a % fromInteger(num_bytes);
match {.top, .byteaddr} = split(pack(a));
Tuple2#(Bit#(SizeOf#(ArrayAddr)), Bit#(SizeOf#(ChipAddr))) route = split(top);
return tuple3(unpack(tpl_1(route)), unpack(tpl_2(route)), unpack(byteaddr));
endfunction endfunction
ByteRAM arrays[num_arrays]; ByteRAM arrays[num_arrays];
@ -179,7 +193,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
Reg#(Maybe#(ArrayAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid); Reg#(Maybe#(ArrayAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid);
Reg#(Maybe#(ArrayAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid); Reg#(Maybe#(ArrayAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid);
interface VRAMServer portA; interface Server portA;
interface Put request; interface Put request;
method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid); method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid);
match {.array, .chip, .byteaddr} = split_addr(req.addr); match {.array, .chip, .byteaddr} = split_addr(req.addr);
@ -196,7 +210,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
endinterface endinterface
endinterface endinterface
interface VRAMServer portB; interface Server portB;
interface Put request; interface Put request;
method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid); method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid);
match {.array, .chip, .byteaddr} = split_addr(req.addr); match {.array, .chip, .byteaddr} = split_addr(req.addr);