3ed048e4f5
Identifying response vnets versus other vnets will allow garnet to determine which vnets will carry data packets, and which will carry ctrl packets, and use appropriate buffer sizes (since data packets are larger than ctrl packets). This in turn allows the orion power model to accurately estimate buffer power.
288 lines
8.7 KiB
Text
288 lines
8.7 KiB
Text
|
|
machine(DMA, "DMA Controller")
|
|
: DMASequencer * dma_sequencer,
|
|
int request_latency = 14,
|
|
int response_latency = 14
|
|
{
|
|
|
|
MessageBuffer goo1, network="From", virtual_network="0", ordered="false", vnet_type="goo";
|
|
MessageBuffer goo2, network="From", virtual_network="1", ordered="false", vnet_type="goo";
|
|
MessageBuffer responseFromDir, network="From", virtual_network="2", ordered="false", vnet_type="response";
|
|
|
|
MessageBuffer foo1, network="To", virtual_network="0", ordered="false", vnet_type="foo";
|
|
MessageBuffer reqToDir, network="To", virtual_network="1", ordered="false", vnet_type="request";
|
|
MessageBuffer respToDir, network="To", virtual_network="2", ordered="false", vnet_type="dmaresponse";
|
|
|
|
state_declaration(State, desc="DMA states", default="DMA_State_READY") {
|
|
READY, AccessPermission:Invalid, desc="Ready to accept a new request";
|
|
BUSY_RD, AccessPermission:Busy, desc="Busy: currently processing a request";
|
|
BUSY_WR, AccessPermission:Busy, desc="Busy: currently processing a request";
|
|
}
|
|
|
|
enumeration(Event, desc="DMA events") {
|
|
ReadRequest, desc="A new read request";
|
|
WriteRequest, desc="A new write request";
|
|
Data, desc="Data from a DMA memory read";
|
|
DMA_Ack, desc="DMA write to memory completed";
|
|
Inv_Ack, desc="Invalidation Ack from a sharer";
|
|
All_Acks, desc="All acks received";
|
|
}
|
|
|
|
structure(TBE, desc="...") {
|
|
Address address, desc="Physical address";
|
|
int NumAcks, default="0", desc="Number of Acks pending";
|
|
DataBlock DataBlk, desc="Data";
|
|
}
|
|
|
|
structure(DMASequencer, external = "yes") {
|
|
void ackCallback();
|
|
void dataCallback(DataBlock);
|
|
}
|
|
|
|
structure(TBETable, external = "yes") {
|
|
TBE lookup(Address);
|
|
void allocate(Address);
|
|
void deallocate(Address);
|
|
bool isPresent(Address);
|
|
}
|
|
|
|
MessageBuffer mandatoryQueue, ordered="false";
|
|
MessageBuffer triggerQueue, ordered="true";
|
|
TBETable TBEs, template_hack="<DMA_TBE>";
|
|
State cur_state;
|
|
|
|
void set_tbe(TBE b);
|
|
void unset_tbe();
|
|
|
|
State getState(TBE tbe, Address addr) {
|
|
return cur_state;
|
|
}
|
|
void setState(TBE tbe, Address addr, State state) {
|
|
cur_state := state;
|
|
}
|
|
|
|
out_port(reqToDirectory_out, RequestMsg, reqToDir, desc="...");
|
|
out_port(respToDirectory_out, ResponseMsg, respToDir, desc="...");
|
|
out_port(foo1_out, ResponseMsg, foo1, desc="...");
|
|
out_port(triggerQueue_out, TriggerMsg, triggerQueue, desc="...");
|
|
|
|
in_port(goo1_in, RequestMsg, goo1) {
|
|
if (goo1_in.isReady()) {
|
|
peek(goo1_in, RequestMsg) {
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
in_port(goo2_in, RequestMsg, goo2) {
|
|
if (goo2_in.isReady()) {
|
|
peek(goo2_in, RequestMsg) {
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
in_port(dmaRequestQueue_in, SequencerMsg, mandatoryQueue, desc="...") {
|
|
if (dmaRequestQueue_in.isReady()) {
|
|
peek(dmaRequestQueue_in, SequencerMsg) {
|
|
if (in_msg.Type == SequencerRequestType:LD ) {
|
|
trigger(Event:ReadRequest, in_msg.LineAddress,
|
|
TBEs[in_msg.LineAddress]);
|
|
} else if (in_msg.Type == SequencerRequestType:ST) {
|
|
trigger(Event:WriteRequest, in_msg.LineAddress,
|
|
TBEs[in_msg.LineAddress]);
|
|
} else {
|
|
error("Invalid request type");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
in_port(dmaResponseQueue_in, ResponseMsg, responseFromDir, desc="...") {
|
|
if (dmaResponseQueue_in.isReady()) {
|
|
peek( dmaResponseQueue_in, ResponseMsg) {
|
|
if (in_msg.Type == CoherenceResponseType:DMA_ACK) {
|
|
trigger(Event:DMA_Ack, makeLineAddress(in_msg.Address),
|
|
TBEs[makeLineAddress(in_msg.Address)]);
|
|
} else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE ||
|
|
in_msg.Type == CoherenceResponseType:DATA) {
|
|
trigger(Event:Data, makeLineAddress(in_msg.Address),
|
|
TBEs[makeLineAddress(in_msg.Address)]);
|
|
} else if (in_msg.Type == CoherenceResponseType:ACK) {
|
|
trigger(Event:Inv_Ack, makeLineAddress(in_msg.Address),
|
|
TBEs[makeLineAddress(in_msg.Address)]);
|
|
} else {
|
|
error("Invalid response type");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trigger Queue
|
|
in_port(triggerQueue_in, TriggerMsg, triggerQueue) {
|
|
if (triggerQueue_in.isReady()) {
|
|
peek(triggerQueue_in, TriggerMsg) {
|
|
if (in_msg.Type == TriggerType:ALL_ACKS) {
|
|
trigger(Event:All_Acks, in_msg.Address, TBEs[in_msg.Address]);
|
|
} else {
|
|
error("Unexpected message");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
action(s_sendReadRequest, "s", desc="Send a DMA read request to memory") {
|
|
peek(dmaRequestQueue_in, SequencerMsg) {
|
|
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
|
out_msg.Address := in_msg.PhysicalAddress;
|
|
out_msg.Type := CoherenceRequestType:DMA_READ;
|
|
out_msg.DataBlk := in_msg.DataBlk;
|
|
out_msg.Len := in_msg.Len;
|
|
out_msg.Destination.add(map_Address_to_Directory(address));
|
|
out_msg.Requestor := machineID;
|
|
out_msg.RequestorMachine := MachineType:DMA;
|
|
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(s_sendWriteRequest, "\s", desc="Send a DMA write request to memory") {
|
|
peek(dmaRequestQueue_in, SequencerMsg) {
|
|
enqueue(reqToDirectory_out, RequestMsg, latency=request_latency) {
|
|
out_msg.Address := in_msg.PhysicalAddress;
|
|
out_msg.Type := CoherenceRequestType:DMA_WRITE;
|
|
out_msg.DataBlk := in_msg.DataBlk;
|
|
out_msg.Len := in_msg.Len;
|
|
out_msg.Destination.add(map_Address_to_Directory(address));
|
|
out_msg.Requestor := machineID;
|
|
out_msg.RequestorMachine := MachineType:DMA;
|
|
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(a_ackCallback, "a", desc="Notify dma controller that write request completed") {
|
|
dma_sequencer.ackCallback();
|
|
}
|
|
|
|
action(o_checkForCompletion, "o", desc="Check if we have received all the messages required for completion") {
|
|
assert(is_valid(tbe));
|
|
if (tbe.NumAcks == 0) {
|
|
enqueue(triggerQueue_out, TriggerMsg) {
|
|
out_msg.Address := address;
|
|
out_msg.Type := TriggerType:ALL_ACKS;
|
|
}
|
|
}
|
|
}
|
|
|
|
action(u_updateAckCount, "u", desc="Update ack count") {
|
|
peek(dmaResponseQueue_in, ResponseMsg) {
|
|
assert(is_valid(tbe));
|
|
tbe.NumAcks := tbe.NumAcks - in_msg.Acks;
|
|
}
|
|
}
|
|
|
|
action( u_sendExclusiveUnblockToDir, "\u", desc="send exclusive unblock to directory") {
|
|
enqueue(respToDirectory_out, ResponseMsg, latency=response_latency) {
|
|
out_msg.Address := address;
|
|
out_msg.Type := CoherenceResponseType:UNBLOCK_EXCLUSIVE;
|
|
out_msg.Destination.add(map_Address_to_Directory(address));
|
|
out_msg.Sender := machineID;
|
|
out_msg.SenderMachine := MachineType:DMA;
|
|
out_msg.MessageSize := MessageSizeType:Writeback_Control;
|
|
}
|
|
}
|
|
|
|
action(p_popRequestQueue, "p", desc="Pop request queue") {
|
|
dmaRequestQueue_in.dequeue();
|
|
}
|
|
|
|
action(p_popResponseQueue, "\p", desc="Pop request queue") {
|
|
dmaResponseQueue_in.dequeue();
|
|
}
|
|
|
|
action(p_popTriggerQueue, "pp", desc="Pop trigger queue") {
|
|
triggerQueue_in.dequeue();
|
|
}
|
|
|
|
action(t_updateTBEData, "t", desc="Update TBE Data") {
|
|
peek(dmaResponseQueue_in, ResponseMsg) {
|
|
assert(is_valid(tbe));
|
|
tbe.DataBlk := in_msg.DataBlk;
|
|
}
|
|
}
|
|
|
|
action(d_dataCallbackFromTBE, "/d", desc="data callback with data from TBE") {
|
|
assert(is_valid(tbe));
|
|
dma_sequencer.dataCallback(tbe.DataBlk);
|
|
}
|
|
|
|
action(v_allocateTBE, "v", desc="Allocate TBE entry") {
|
|
TBEs.allocate(address);
|
|
set_tbe(TBEs[address]);
|
|
}
|
|
|
|
action(w_deallocateTBE, "w", desc="Deallocate TBE entry") {
|
|
TBEs.deallocate(address);
|
|
unset_tbe();
|
|
}
|
|
|
|
action(z_stall, "z", desc="dma is busy..stall") {
|
|
// do nothing
|
|
}
|
|
|
|
|
|
|
|
transition(READY, ReadRequest, BUSY_RD) {
|
|
s_sendReadRequest;
|
|
v_allocateTBE;
|
|
p_popRequestQueue;
|
|
}
|
|
|
|
transition(BUSY_RD, Inv_Ack) {
|
|
u_updateAckCount;
|
|
o_checkForCompletion;
|
|
p_popResponseQueue;
|
|
}
|
|
|
|
transition(BUSY_RD, Data, READY) {
|
|
t_updateTBEData;
|
|
d_dataCallbackFromTBE;
|
|
w_deallocateTBE;
|
|
//u_updateAckCount;
|
|
//o_checkForCompletion;
|
|
p_popResponseQueue;
|
|
}
|
|
|
|
transition(BUSY_RD, All_Acks, READY) {
|
|
d_dataCallbackFromTBE;
|
|
//u_sendExclusiveUnblockToDir;
|
|
w_deallocateTBE;
|
|
p_popTriggerQueue;
|
|
}
|
|
|
|
transition(READY, WriteRequest, BUSY_WR) {
|
|
s_sendWriteRequest;
|
|
v_allocateTBE;
|
|
p_popRequestQueue;
|
|
}
|
|
|
|
transition(BUSY_WR, Inv_Ack) {
|
|
u_updateAckCount;
|
|
o_checkForCompletion;
|
|
p_popResponseQueue;
|
|
}
|
|
|
|
transition(BUSY_WR, DMA_Ack) {
|
|
u_updateAckCount; // actually increases
|
|
o_checkForCompletion;
|
|
p_popResponseQueue;
|
|
}
|
|
|
|
transition(BUSY_WR, All_Acks, READY) {
|
|
a_ackCallback;
|
|
u_sendExclusiveUnblockToDir;
|
|
w_deallocateTBE;
|
|
p_popTriggerQueue;
|
|
}
|
|
}
|