2024-09-08 18:26:59 +02:00
|
|
|
package VRAM;
|
|
|
|
|
|
|
|
import Connectable::*;
|
|
|
|
import GetPut::*;
|
|
|
|
import ClientServer::*;
|
|
|
|
import Vector::*;
|
|
|
|
import FIFOF::*;
|
|
|
|
import SpecialFIFOs::*;
|
|
|
|
|
|
|
|
import MemArbiter::*;
|
|
|
|
import VRAMCore::*;
|
|
|
|
|
|
|
|
// Re-exports from VRAMCore
|
|
|
|
export VRAMAddr, VRAMData, VRAMRequest(..), VRAMResponse(..);
|
|
|
|
|
|
|
|
export VRAMServer(..);
|
|
|
|
export VRAM(..), mkVRAM;
|
|
|
|
|
2024-09-15 01:40:14 +02:00
|
|
|
export mkArbitratedVRAMServers;
|
|
|
|
|
2024-09-09 08:42:16 +02:00
|
|
|
// A VRAMServer is a memory port.
|
2024-09-08 18:26:59 +02:00
|
|
|
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
|
|
|
|
|
|
|
|
// mkArbitratedVRAMServers expands a VRAMServer port into multiple
|
|
|
|
// ports through the use of a MemArbiter.
|
|
|
|
module mkArbitratedVRAMServers(VRAMServer ram, MemArbiter#(n, VRAMAddr) arb, Vector#(n, VRAMServer) ifc)
|
|
|
|
provisos (Min#(n, 1, 1),
|
|
|
|
Alias#(port_idx, UInt#(TLog#(n))));
|
|
|
|
Vector#(n, FIFOF#(VRAMRequest)) requests <- replicateM(mkBypassFIFOF());
|
|
|
|
Vector#(n, FIFOF#(VRAMResponse)) responses <- replicateM(mkBypassFIFOF());
|
|
|
|
Reg#(Maybe#(port_idx)) awaiting_response[2] <- mkCReg(2, tagged Invalid);
|
|
|
|
|
|
|
|
(* fire_when_enabled *)
|
|
|
|
rule request_ports;
|
|
|
|
for (Integer i=0; i<valueOf(n); i=i+1)
|
|
|
|
if (requests[i].notEmpty) begin
|
|
|
|
let req = requests[i].first;
|
|
|
|
let arb_req = MemArbiterOp{
|
|
|
|
write: isValid(req.data),
|
|
|
|
addr: req.addr
|
|
|
|
};
|
|
|
|
arb.ports[i].request(arb_req);
|
|
|
|
end
|
|
|
|
endrule
|
|
|
|
|
|
|
|
(* fire_when_enabled *)
|
|
|
|
rule submit (awaiting_response[1] matches tagged Invalid);
|
|
|
|
let port = arb.granted_port();
|
2024-09-15 01:40:14 +02:00
|
|
|
let req = requests[port].first;
|
|
|
|
ram.request.put(req);
|
2024-09-08 18:26:59 +02:00
|
|
|
requests[port].deq();
|
2024-09-15 01:40:14 +02:00
|
|
|
// Only reads generate a response.
|
|
|
|
if (req.data matches tagged Invalid)
|
|
|
|
awaiting_response[1] <= tagged Valid port;
|
2024-09-08 18:26:59 +02:00
|
|
|
endrule
|
|
|
|
|
|
|
|
(* fire_when_enabled *)
|
2024-09-09 08:42:16 +02:00
|
|
|
rule response (awaiting_response[0] matches tagged Valid .port);
|
2024-09-08 18:26:59 +02:00
|
|
|
let resp <- ram.response.get();
|
|
|
|
responses[port].enq(resp);
|
|
|
|
awaiting_response[0] <= tagged Invalid;
|
|
|
|
endrule
|
|
|
|
|
|
|
|
return map(uncurry(toGPServer), zip(requests, responses));
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
// VRAM is a GARY video RAM and its memory ports.
|
|
|
|
interface VRAM;
|
|
|
|
interface VRAMServer cpu;
|
|
|
|
interface VRAMServer debugger;
|
|
|
|
interface VRAMServer palette;
|
|
|
|
interface VRAMServer tile1;
|
|
|
|
interface VRAMServer tile2;
|
|
|
|
interface VRAMServer sprite;
|
|
|
|
endinterface
|
|
|
|
|
2024-09-09 08:44:38 +02:00
|
|
|
// mkVRAM constructs a VRAM of the requested size. The memory size must be a multiple of
|
|
|
|
// 4KiB, with a maximum of 128KiB.
|
|
|
|
//
|
|
|
|
// Memory accesses are spread across two internal ports as follows:
|
2024-09-08 18:26:59 +02:00
|
|
|
//
|
|
|
|
// Port A: strict most-important-wins priority: CPU, then debugger,
|
|
|
|
// then palette DAC.
|
|
|
|
// Port B: equal round-robin prioritization between two tile engines
|
|
|
|
// and the sprite engine.
|
|
|
|
module mkVRAM(Integer num_kilobytes, VRAM ifc);
|
|
|
|
VRAMCore ram <- mkVRAMCore(num_kilobytes);
|
|
|
|
|
|
|
|
MemArbiter#(3, VRAMAddr) arbA <- mkPriorityMemArbiter();
|
|
|
|
Vector#(3, VRAMServer) portA <- mkArbitratedVRAMServers(ram.portA, arbA);
|
|
|
|
|
|
|
|
MemArbiter#(3, VRAMAddr) arbB <- mkRoundRobinMemArbiter();
|
|
|
|
Vector#(3, VRAMServer) portB <- mkArbitratedVRAMServers(ram.portB, arbB);
|
|
|
|
|
2024-09-09 08:44:38 +02:00
|
|
|
// Connect the arbiters together so they correctly prevent
|
|
|
|
// write-write and write-read conflicts.
|
2024-09-08 18:26:59 +02:00
|
|
|
mkConnection(arbA, arbB);
|
|
|
|
|
|
|
|
interface cpu = portA[0];
|
|
|
|
interface debugger = portA[1];
|
|
|
|
interface palette = portA[2];
|
|
|
|
interface tile1 = portB[0];
|
|
|
|
interface tile2 = portB[1];
|
|
|
|
interface sprite = portB[2];
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
endpackage
|