vram/VRAM: early VRAM implementation
Only checked up to mkByteRAMArray, main VRAM still WIP
This commit is contained in:
parent
7560199251
commit
f7cb4b6ba2
|
@ -0,0 +1,38 @@
|
||||||
|
package Top;
|
||||||
|
|
||||||
|
import VRAM::*;
|
||||||
|
import ECP5_RAM::*;
|
||||||
|
import TriState::*;
|
||||||
|
|
||||||
|
(* always_enabled *)
|
||||||
|
interface Top;
|
||||||
|
method Action phi2(bit v);
|
||||||
|
method Action we(bit we);
|
||||||
|
method Action addr(UInt#(24) addr);
|
||||||
|
interface InOut#(Bit#(8)) data();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTop(Top);
|
||||||
|
Reg#(PortReq) reqA <- mkRegU();
|
||||||
|
Reg#(VRAMData) respA <- mkRegU();
|
||||||
|
|
||||||
|
let _ret <- mkByteRAMArray(8);
|
||||||
|
|
||||||
|
rule putA;
|
||||||
|
_ret.portA.put(reqA.chip_select, reqA.write, reqA.addr, reqA.datain);
|
||||||
|
endrule
|
||||||
|
|
||||||
|
rule getA;
|
||||||
|
respA <= _ret.portA.read();
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method portA_read = respA._read;
|
||||||
|
method Action portA_put(cs, w, a, d);
|
||||||
|
reqA <= PortReq{chip_select: cs, write: w, addr: a, datain: d};
|
||||||
|
endmethod
|
||||||
|
method portB_read = _ret.portB.read;
|
||||||
|
method portB_put = _ret.portB.put;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,181 @@
|
||||||
|
package VRAM;
|
||||||
|
|
||||||
|
import GetPut::*;
|
||||||
|
import ClientServer::*;
|
||||||
|
import DReg::*;
|
||||||
|
import BRAM::*;
|
||||||
|
import Vector::*;
|
||||||
|
import FIFOF::*;
|
||||||
|
import SpecialFIFOs::*;
|
||||||
|
|
||||||
|
import DelayLine::*;
|
||||||
|
import ECP5_RAM::*;
|
||||||
|
|
||||||
|
typedef UInt#(17) VRAMAddr;
|
||||||
|
|
||||||
|
typedef Bit#(8) VRAMData;
|
||||||
|
|
||||||
|
// Each byte RAM we build below can address 4096 bytes, which is 12
|
||||||
|
// address bits.
|
||||||
|
typedef UInt#(12) ByteAddr;
|
||||||
|
|
||||||
|
// The difference between ByteRAM_Addr and VRAMAddr is the chip
|
||||||
|
// select ID.
|
||||||
|
typedef UInt#(5) ChipAddr;
|
||||||
|
|
||||||
|
// ByteRAM is two EBRs glued together to make a whole-byte memory.
|
||||||
|
typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM;
|
||||||
|
|
||||||
|
// mkByteRAM glues two ECP5 EBRs together to make a 4096x8b memory
|
||||||
|
// block. Like the underlying ECP5 EBRs, callers must bring their own
|
||||||
|
// flow control to read out responses one cycle after putting a read
|
||||||
|
// request.
|
||||||
|
module mkByteRAM(UInt#(3) chip_addr, ByteRAM ifc);
|
||||||
|
EBRPortConfig cfg = defaultValue;
|
||||||
|
cfg.chip_select_addr = 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;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, ByteAddr addr, VRAMData data_in);
|
||||||
|
upper.portA.put(chip_select, write, addr, truncate(data_in>>4));
|
||||||
|
lower.portA.put(chip_select, write, addr, truncate(data_in));
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method VRAMData read();
|
||||||
|
return (extend(upper.portA.read())<<4) | (extend(lower.portA.read()));
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface EBRPort portB;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, ByteAddr addr, VRAMData data_in);
|
||||||
|
upper.portB.put(chip_select, write, addr, truncate(data_in>>4));
|
||||||
|
lower.portB.put(chip_select, write, addr, truncate(data_in));
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method VRAMData read();
|
||||||
|
return (extend(upper.portB.read())<<4) | (extend(lower.portB.read()));
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endmodule : mkByteRAM
|
||||||
|
|
||||||
|
module mkByteRAMArray(Integer num_chips, ByteRAM ifc);
|
||||||
|
if (num_chips > 8)
|
||||||
|
error("mkByteRAMArray can only array 8 raw ByteRAMs");
|
||||||
|
|
||||||
|
ByteRAM blocks[num_chips];
|
||||||
|
for (Integer i=0; i<num_chips; i=i+1)
|
||||||
|
blocks[i] <- mkByteRAM(fromInteger(i));
|
||||||
|
|
||||||
|
DelayLine#(UInt#(3)) read_chip_A <- mkDelayLine(1);
|
||||||
|
DelayLine#(UInt#(3)) read_chip_B <- mkDelayLine(1);
|
||||||
|
|
||||||
|
interface EBRPort portA;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, ByteAddr addr, VRAMData data_in);
|
||||||
|
for (Integer i=0; i<num_chips; i=i+1)
|
||||||
|
blocks[i].portA.put(chip_select, write, addr, data_in);
|
||||||
|
if (write)
|
||||||
|
read_chip_A <= chip_select;
|
||||||
|
endmethod
|
||||||
|
method VRAMData read();
|
||||||
|
if (read_chip_A.ready)
|
||||||
|
if (read_chip_A <= fromInteger(num_chips-1))
|
||||||
|
return blocks[read_chip_A].portA.read();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface EBRPort portB;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, ByteAddr addr, VRAMData data_in);
|
||||||
|
for (Integer i=0; i<num_chips; i=i+1)
|
||||||
|
blocks[i].portB.put(chip_select, write, addr, data_in);
|
||||||
|
if (write)
|
||||||
|
read_chip_B <= chip_select;
|
||||||
|
endmethod
|
||||||
|
method VRAMData read();
|
||||||
|
if (read_chip_B.ready)
|
||||||
|
if (read_chip_B <= fromInteger(num_chips-1))
|
||||||
|
return blocks[read_chip_B].portB.read();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
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 VRAM;
|
||||||
|
interface VRAMServer portA;
|
||||||
|
interface VRAMServer portB;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module mkVRAM(Integer num_4kB_blocks, VRAM ifc);
|
||||||
|
if (num_4kB_blocks > 32)
|
||||||
|
error("maximum number of blocks is 32 (128KiB)");
|
||||||
|
UInt#(TAdd#(SizeOf#(VRAMAddr), 1)) max_request_addr = fromInteger((4096 * num_4kB_blocks));
|
||||||
|
|
||||||
|
function Tuple2#(ChipAddr, ByteAddr) split_addr(VRAMAddr a);
|
||||||
|
UInt#(TAdd#(SizeOf#(VRAMAddr), 1)) expanded = extend(a);
|
||||||
|
VRAMAddr wrapped = truncate(expanded % max_request_addr);
|
||||||
|
match {.chip, .off} = split(pack(wrapped));
|
||||||
|
return tuple2(unpack(chip), unpack(off));
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
ByteRAM blocks[num_4kB_blocks];
|
||||||
|
for (Integer i=0; i<num_4kB_blocks; i=i+1)
|
||||||
|
blocks[i] <- mkByteRAM(0);
|
||||||
|
|
||||||
|
Reg#(Maybe#(ChipAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid);
|
||||||
|
Reg#(Maybe#(ChipAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid);
|
||||||
|
|
||||||
|
interface VRAMServer portA;
|
||||||
|
interface Put request;
|
||||||
|
method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid);
|
||||||
|
match {.chip, .off} = split_addr(req.addr);
|
||||||
|
blocks[chip].portA.put(0, isValid(req.data), off, fromMaybe(0, req.data));
|
||||||
|
if (!isValid(req.data))
|
||||||
|
inflight_A[1] <= tagged Valid chip;
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
interface Get response;
|
||||||
|
method ActionValue#(VRAMResponse) get() if (inflight_A[0] matches tagged Valid .chip);
|
||||||
|
inflight_A[0] <= tagged Invalid;
|
||||||
|
return VRAMResponse{data: blocks[chip].portA.read()};
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface VRAMServer portB;
|
||||||
|
interface Put request;
|
||||||
|
method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid);
|
||||||
|
match {.chip, .off} = split_addr(req.addr);
|
||||||
|
blocks[chip].portB.put(0, isValid(req.data), off, fromMaybe(0, req.data));
|
||||||
|
if (!isValid(req.data))
|
||||||
|
inflight_B[1] <= tagged Valid chip;
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
interface Get response;
|
||||||
|
method ActionValue#(VRAMResponse) get() if (inflight_B[0] matches tagged Valid .chip);
|
||||||
|
inflight_B[0] <= tagged Invalid;
|
||||||
|
return VRAMResponse{data: blocks[chip].portB.read()};
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endinterface
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
Loading…
Reference in New Issue