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