Bluepsec Examples
Avalon Master and Slave Interfaces
Overview
Altera's SOPC Builder system allows rapid design of systems-on-FPGA, including processors, interface devices, etc. Components are hooked together using the Avalon interconnect. This library (Avalon2ClientServer) provides Avalon master and slave interfaces for single memory accesses (not bursts). An example bridge design is also presented.
The AvalonFIFOs example provides memory mapped communication channels between multiple processors. This code could probably do with a little tidying, but the design as it stands is robust (we're using it in a research project).
Avalon2ClientServer (Master and Slave Interfaces)
/***************************************************************************** Avalon2ClientServer =================== Provides Avalon (Altera's switched bus standard) slave and master interfaces to Bluespec Client and Server interfaces *****************************************************************************/ package Avalon2ClientServer; import FIFO::*; import FIFOF::*; import FIFOLevel::*; import GetPut::*; import ClientServer::*; import Connectable::*; // Type for avalon bus data typedef UInt#(32) AvalonWordT; typedef Maybe#(AvalonWordT) ReturnedDataT; // Memory access type. Note that MemNull used as part of arbiterlock release message. typedef enum { MemRead, MemWrite, MemNull } MemAccessT deriving(Bits,Eq); // Structure for memory requests typedef struct { MemAccessT rw; UInt#(word_address_width) addr; // word address AvalonWordT data; Bool arbiterlock; } MemAccessPacketT#(numeric type word_address_width) deriving(Bits,Eq); /***************************************************************************** Avalon slave interface to Bluepsec Client interface =================================================== Simon Moore, September 2009 *****************************************************************************/ // Avalon Slave Interface // notes: // - all methods are ready and enabled // - names are chosen to match what SOPC builder expects for variable names // in the Verilog code - don't change! (* always_ready, always_enabled *) interface AvalonSlaveIfc#(numeric type word_address_width); method Action s0(UInt#(word_address_width) address, AvalonWordT writedata, Bool write, Bool read, Bool arbiterlock); //, Bool resetrequest); method AvalonWordT s0_readdata; method Bool s0_waitrequest; endinterface interface AvalonSlave2ClientIfc#(numeric type word_address_width); interface AvalonSlaveIfc#(word_address_width) avs; interface Client#(MemAccessPacketT#(word_address_width),ReturnedDataT) client; //(* always_read, always_enabled *) method Bool reset_from_bus; endinterface module mkAvalonSlave2Client(AvalonSlave2ClientIfc#(word_address_width)) provisos(Max#(word_address_width,29,29)); // bypass wires for incoming Avalon slave signals Wire#(UInt#(word_address_width)) address_w <- mkBypassWire; Wire#(AvalonWordT) writedata_w <- mkBypassWire; Wire#(Bool) read_w <- mkBypassWire; Wire#(Bool) write_w <- mkBypassWire; Wire#(Bool) arbiterlock_w <- mkBypassWire; Reg# (Bool) prev_arbiterlock <- mkReg(False); // Wire#(Bool) resetrequest_w <- mkBypassWire; // bypass wire for Avalon wait signal + pulsewires to clear Wire#(Bool) avalonwait <- mkBypassWire; PulseWire avalonwait_end_read <- mkPulseWire; PulseWire avalonwait_end_write <- mkPulseWire; // DWire for read data returned to Avalon slave bus Wire#(AvalonWordT) datareturned <- mkDWire(32'hdeaddead); // reg indicating that the Avalon request is being processed and further // requests should be ignored until the avalonwait signal has been released // (gone low) Reg#(Bool) ignore_further_requests <- mkReg(False); // FIFO holding requests received from Avalon slave bus sent out via // the client request interface FIFOF#(MemAccessPacketT#(word_address_width)) outbuf <- mkFIFOF; // provide the avalonwait signal // note: this must appear within the same clock cycle that a read or write // is initiated (* no_implicit_conditions *) rule wire_up_avalonwait; avalonwait <= (read_w && !avalonwait_end_read) || (write_w && !avalonwait_end_write); endrule rule arbiterlock_history; prev_arbiterlock <= arbiterlock_w; endrule rule handle_end_arbiterlock (prev_arbiterlock && !arbiterlock_w && !read_w && !write_w); outbuf.enq(MemAccessPacketT{ rw: MemNull, // send MemNull to clear arbiter lock addr: address_w, // don't care what the address and data are but keep... data: writedata_w, // ...consistent with next rule to simplify implementation arbiterlock: arbiterlock_w}); endrule // if this is a new Avalon slave bus request then enqueue // note: if outbuf FIFO is full, Avalon slave forced to wait rule hanlde_bus_requests ((read_w || write_w) && !ignore_further_requests); outbuf.enq(MemAccessPacketT{ rw: read_w ? MemRead : MemWrite, addr: address_w, data: writedata_w, // N.B. "data" is undefined for reads arbiterlock: arbiterlock_w}); ignore_further_requests <= read_w; // release avalonwait for writes since the request has been enqueued if(write_w) avalonwait_end_write.send; endrule // once avalonwait has gone low, get ready to respond to next request // from the Avalon bus rule cancel_ingore_further_requests(!avalonwait && ignore_further_requests); ignore_further_requests <= False; endrule // Avalon slave interface - just wiring interface AvalonSlaveIfc avs; method Action s0(address, writedata, write, read, arbiterlock); //, resetrequest); address_w <= address; writedata_w <= writedata; write_w <= write; read_w <= read; arbiterlock_w <= arbiterlock; // resetrequest_w <= resetrequest; endmethod method s0_readdata; return datareturned; endmethod method s0_waitrequest; return avalonwait; endmethod endinterface // client interface interface Client client; interface request = toGet(outbuf); interface Put response; method Action put(d); // note: respond to data read // currently if d is Invalid then ignored but it could be used // to do a avalonwait_end_write.send if it was required the // clients waited on writes until the writes had completed if(isValid(d)) begin // note duality of DWire for data and PulseWire for // associated signal datareturned <= fromMaybe(32'hdeaddead,d); avalonwait_end_read.send; end endmethod endinterface endinterface // method Bool reset_from_bus; // return resetrequest_w; // endmethod endmodule /***************************************************************************** Bluespec Server interface to Avalon master interface ==================================================== Simon Moore, October 2009 *****************************************************************************/ // Avalon Master Interface // notes: // - all methods are ready and enabled // - names are chosen to match what SOPC builder expects for variable names // in the Verilog code - don't change! // - initally a long latency (too much buffering?) but (hopfully) robust // design remove some latch stages in the future (* always_ready, always_enabled *) interface AvalonMasterIfc#(numeric type word_address_width); method Action m0(AvalonWordT readdata, Bool waitrequest); method AvalonWordT m0_writedata; method UInt#(TAdd#(2,word_address_width)) m0_address; method Bool m0_read; method Bool m0_write; method Bool m0_arbiterlock; endinterface interface Server2AvalonMasterIfc#(numeric type word_address_width); interface AvalonMasterIfc#(word_address_width) avm; interface Server#(MemAccessPacketT#(word_address_width),ReturnedDataT) server; endinterface module mkServer2AvalonMaster(Server2AvalonMasterIfc#(word_address_width)) provisos(Max#(word_address_width,29,29), Add#(word_address_width, 2, TAdd#(2, word_address_width))); // bypass wires for incoming Avalon master signals // N.B. avalon master address is a byte address, so need to add 2 bits Reg#(UInt#(word_address_width)) address_r <- mkReg(0); Reg#(AvalonWordT) writedata_r <- mkReg(0); Reg#(Bool) read_r <- mkReg(False); Reg#(Bool) write_r <- mkReg(False); Reg#(Bool) arbiterlock_r <- mkReg(False); PulseWire signal_read <- mkPulseWire; PulseWire signal_write <- mkPulseWire; Wire#(Bool) avalonwait <- mkBypassWire; Wire#(AvalonWordT) avalonreaddata <- mkBypassWire; // buffer data returned // TODO: could this buffer be removed by not initiating the transaction // until the returndata get operation was active, then do the memory // transaction and return the value to the get without buffering? // - possibly not if the interface is fully pipelined because there // can be several transactions ongoing (several addresses issued, etc.) // before data comes back // FIFO of length 4 which is: // Unguarded enq since it it guarded by the bus transaction initiation // Guarded deq // Unguarded count so isLessThan will not block FIFOLevelIfc#(ReturnedDataT,4) datareturnbuf <- mkGFIFOLevel(True,False,True); FIFO#(MemAccessT) pending_acks <- mkFIFO; let write_ack = write_r && !read_r && !avalonwait; let read_ack = !write_r && read_r && !avalonwait; rule buffer_data_read (read_ack && (pending_acks.first==MemRead)); datareturnbuf.enq(tagged Valid avalonreaddata); $display(" %05t: Avalon2ClientServer returning data",$time); pending_acks.deq; endrule rule signal_data_write (write_ack && (pending_acks.first==MemWrite)); datareturnbuf.enq(tagged Invalid); // signal write has happened pending_acks.deq; endrule rule signal_mem_null (pending_acks.first==MemNull); datareturnbuf.enq(tagged Invalid); // signal null has happened pending_acks.deq; endrule (* no_implicit_conditions *) rule do_read_reg; if(signal_read) read_r <= True; else if(!avalonwait) read_r <= False; endrule (* no_implicit_conditions *) rule do_write_reg; if(signal_write) write_r <= True; else if(!avalonwait) write_r <= False; endrule // Avalon master interface - just wiring interface AvalonMasterIfc avm; method Action m0(readdata, waitrequest); avalonreaddata <= readdata; avalonwait <= waitrequest; endmethod method m0_writedata; return writedata_r; endmethod method m0_address; return unpack({pack(address_r),2'b00}); endmethod method m0_read; return read_r; endmethod method m0_write; return write_r; endmethod method m0_arbiterlock; return arbiterlock_r; endmethod endinterface // server interface interface Server server; interface response = toGet(datareturnbuf); interface Put request; method Action put(packet) if (!avalonwait && datareturnbuf.isLessThan(2)); address_r <= packet.addr; writedata_r <= packet.data; arbiterlock_r <= packet.arbiterlock; pending_acks.enq(packet.rw); case(packet.rw) MemRead: signal_read.send(); MemWrite: signal_write.send(); endcase endmethod endinterface endinterface endmodule /***************************************************************************** Bluespec Server interface to Avalon master PIPELINED interface ============================================================== Simon Moore, October 2009 *****************************************************************************/ // Avalon Master Interface - pipelined version // - partially working - really need "flush" signal // notes: // - all methods are ready and enabled // - names are chosen to match what SOPC builder expects for variable names // in the Verilog code - don't change! // - initally a long latency (too much buffering?) but (hopfully) robust // design remove some latch stages in the future (* always_ready, always_enabled *) interface AvalonPipelinedMasterIfc#(numeric type word_address_width); method Action m0(AvalonWordT readdata, Bool readdatavalid, Bool waitrequest); method AvalonWordT m0_writedata; method UInt#(TAdd#(2,word_address_width)) m0_address; method Bool m0_read; method Bool m0_write; method Bool m0_arbiterlock; endinterface interface Server2AvalonPipelinedMasterIfc#(numeric type word_address_width); interface AvalonPipelinedMasterIfc#(word_address_width) avm; interface Server#(MemAccessPacketT#(word_address_width),ReturnedDataT) server; endinterface module mkServer2AvalonPipelinedMaster(Server2AvalonPipelinedMasterIfc#(word_address_width)) provisos(Max#(word_address_width,29,29), Add#(word_address_width, 2, TAdd#(2, word_address_width))); // bypass wires for incoming Avalon master signals // N.B. avalon master address is a byte address, so need to add 2 bits Reg#(UInt#(word_address_width)) address_r <- mkReg(0); Reg#(AvalonWordT) writedata_r <- mkReg(0); Reg#(Bool) read_r <- mkReg(False); Reg#(Bool) write_r <- mkReg(False); Reg#(Bool) arbiterlock_r <- mkReg(False); PulseWire signal_read <- mkPulseWire; PulseWire signal_write <- mkPulseWire; Wire#(Bool) avalonwait <- mkBypassWire; Wire#(Bool) avalonreadvalid <- mkBypassWire; Wire#(AvalonWordT) avalonreaddata <- mkBypassWire; // buffer data returned // TODO: could this buffer be removed by not initiating the transaction // until the returndata get operation was active, then do the memory // transaction and return the value to the get without buffering? // - possibly not if the interface is fully pipelined because there // can be several transactions ongoing (several addresses issued, etc.) // before data comes back // FIFO of length 4 which is: // Unguarded enq since it it guarded by the bus transaction initiation // Guarded deq // Unguarded count so isLessThan will not block FIFOLevelIfc#(ReturnedDataT,4) datareturnbuf <- mkGFIFOLevel(True,False,True); FIFO#(MemAccessT) pending_acks <- mkSizedFIFO(4); FIFO#(MemAccessT) pending_write_acks <- mkSizedFIFO(4); let write_ack = write_r && !read_r && !avalonwait; rule buffer_data_read (avalonreadvalid && (pending_acks.first==MemRead)); datareturnbuf.enq(tagged Valid avalonreaddata); $display(" %05t: Avalon2ClientServer returning data",$time); pending_acks.deq; endrule rule data_read_error (avalonreadvalid && (pending_acks.first!=MemRead)); $display("ERROR: Server2AvalonPipelinedMaster - read returned when expeting a write or null ack"); endrule rule buffer_data_write_during_readvalid (avalonreadvalid && write_ack); pending_write_acks.enq(MemWrite); endrule rule signal_data_write (!avalonreadvalid && write_ack && (pending_acks.first==MemWrite)); datareturnbuf.enq(tagged Invalid); // signal write has happened pending_acks.deq; endrule rule signal_mem_null (pending_acks.first==MemNull); datareturnbuf.enq(tagged Invalid); // signal null has happened pending_acks.deq; endrule rule resolve_pending_write_acks (!avalonreadvalid && !write_ack && (pending_acks.first==MemWrite)); pending_write_acks.deq; // N.B. only fires if this dequeue can happen datareturnbuf.enq(tagged Invalid); pending_acks.deq; endrule (* no_implicit_conditions *) rule do_read_reg; if(signal_read) read_r <= True; else if(!avalonwait) read_r <= False; endrule (* no_implicit_conditions *) rule do_write_reg; if(signal_write) write_r <= True; else if(!avalonwait) write_r <= False; endrule // Avalon master interface - just wiring interface AvalonPipelinedMasterIfc avm; method Action m0(readdata, readdatavalid, waitrequest); avalonreaddata <= readdata; avalonreadvalid <= readdatavalid; avalonwait <= waitrequest; endmethod method m0_writedata; return writedata_r; endmethod method m0_address; return unpack({pack(address_r),2'b00}); endmethod method m0_read; return read_r; endmethod method m0_write; return write_r; endmethod method m0_arbiterlock; return arbiterlock_r; endmethod endinterface // server interface interface Server server; interface response = toGet(datareturnbuf); interface Put request; method Action put(packet) if (!avalonwait && datareturnbuf.isLessThan(2)); address_r <= packet.addr; writedata_r <= packet.data; arbiterlock_r <= packet.arbiterlock; pending_acks.enq(packet.rw); case(packet.rw) MemRead: signal_read.send(); MemWrite: signal_write.send(); endcase endmethod endinterface endinterface endmodule /***************************************************************************** Avalon Bridge N.B. as usual the names on interfaces are chosen to match what SOPC builder expects, so don't change! ****************************************************************************/ interface AvalonBridgeIfc#(numeric type word_address_width); interface AvalonSlaveIfc#(word_address_width) avs; interface AvalonMasterIfc#(word_address_width) avm; endinterface module mkAvalonBridge(AvalonBridgeIfc#(word_address_width)) provisos(Max#(word_address_width,29,29), Add#(word_address_width, 2, TAdd#(2, word_address_width))); AvalonSlave2ClientIfc#(word_address_width) client <- mkAvalonSlave2Client; Server2AvalonMasterIfc#(word_address_width) server <- mkServer2AvalonMaster; mkConnection(client.client,server.server); interface avs = client.avs; interface avm = server.avm; endmodule endpackage
Link to the Avalon2ClientServer.bsv source
AvalonBridge Example
This provides a bridge between master and slave ports whilst introducing one cycle of latency.
/****************************************************************************** Avalon Bridge Example ==================== Simon Moore Version 3 - October 2009 Provides a simple Avalon bridge (no bursts) for Altera's NIOS SOPC system Notes: * should import directly into SOPC builder with default timings * names for parameters match up with the default names that Altera's SOPC builder tool expects - Reference: SOPC builder, Chapter 6, table 6-1 (for default names) Avalon interface specifications (for signals) ******************************************************************************/ package AvalonBridgeExample; import Clocks::*; import FIFOF::*; import Avalon2ClientServer::*; import ClientServer::*; import GetPut::*; (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) (* doc="************** Simon's comments for Verilog code: **************" *) (* doc="Top level module for Avalon Bridge Example" *) (* doc="Port names should align with SOPC builder's new device requirements" *) (* doc="Need to add the $BLUESPECDIR/Verilog directory to the Libraries in Quartus" *) module mkAvalonBridgeExample(AvalonBridgeIfc#(8)); // make bridge with 8-bit word address AvalonBridgeIfc#(8) bridge <- mkAvalonBridge; // N.B. names match what SOPC builder expects interface avs = bridge.avs; interface avm = bridge.avm; endmodule endpackage
Link to the AvalonBridgeExample.bsv source
AvalonTimer Example
This provides a simple timer (clock) that can be started, stopped, set and read over the Avalon interconnect. Code for a NIOS II processor is also provided.
package AvalonTimer; import GetPut::*; import ClientServer::*; import Avalon2ClientServer::*; // word address width `define addr_width 2 (* always_ready, always_enabled *) interface AvalonTimerIfc; interface AvalonSlaveIfc#(`addr_width) avs; // AvalonSlave physical interface method UInt#(26) coe_leds; // exported signals (coe=Conduit Output Export) endinterface // tell Bluespec to produce a synthesizable design // and set clock and reset names to what SOPC builder expects (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) module mkAvalonTimer(AvalonTimerIfc); AvalonSlave2ClientIfc#(`addr_width) slave <- mkAvalonSlave2Client; Reg#(UInt#(32)) timer <- mkReg(0); Reg#(Bool) run_timer <- mkReg(False); Wire#(Maybe#(UInt#(32))) set_timer <- mkDWire(tagged Invalid); PulseWire timer_stop <- mkPulseWire; PulseWire timer_start <- mkPulseWire; rule handle_avalon_requests; let req <- slave.client.request.get(); ReturnedDataT rtn = tagged Invalid; case(tuple2(req.addr, req.rw)) tuple2(0, MemWrite) : set_timer <= tagged Valid req.data; tuple2(0, MemRead) : rtn = tagged Valid timer; tuple2(1, MemWrite) : if(req.data==0) timer_stop.send; else timer_start.send; tuple2(1, MemRead) : begin rtn = tagged Valid timer; timer_stop.send(); end endcase slave.client.response.put(rtn); endrule (* no_implicit_conditions *) rule handle_timer (True); let running = !timer_stop && (timer_start || run_timer); if(isValid(set_timer)) timer <= fromMaybe(?,set_timer); else if(running) timer <= timer+1; run_timer <= running; endrule // pass physical Avalon interface out of this module interface avs = slave.avs; // export upper bits of timer to LEDs method UInt#(26) coe_leds; return truncate(timer>>6); endmethod endmodule endpackage: AvalonTimer
Link to the AvalonTimer.bsv source
#include <stdio.h> // library with hardware specific parameters #include <system.h> // low-level input/output library #include <io.h> void set_timer(int t) { // write t directly to the device bypassing the processor's cache IOWR_32DIRECT(MKAVALONTIMER_0_BASE, 0, t); } int read_timer() { // read the timer directly from the device bypassing the processor's cache return IORD_32DIRECT(MKAVALONTIMER_0_BASE, 0); } void stop_timer() { // stop timer by writing 0 to word address 1 of the device (byte address 4) IOWR_32DIRECT(MKAVALONTIMER_0_BASE, 4, 0); } void start_timer() { // start timer by writing 1 to word address 1 of the device (byte address 4) IOWR_32DIRECT(MKAVALONTIMER_0_BASE, 4, 1); } int read_and_stop_timer() { return IORD_32DIRECT(MKAVALONTIMER_0_BASE, 4); } int main() { printf("Starting timer test\n"); fprintf(stderr,"Timer test\n(LCD output)"); stop_timer(); printf("timer = %d\n",read_timer()); printf("timer = %d\n",read_timer()); printf("start timer\n"); start_timer(); printf("timer = %d\n",read_timer()); printf("timer = %d\n",read_timer()); printf("read and stop = %d\n",read_and_stop_timer()); printf("timer = %d\n",read_timer()); return 0; }
Link to the timereg.c source
AvalonFIFOs Example
/***************************************************************************** AvalonFIFOs =========== Simon Moore, Dec 2009 Dec 2009 - added multiported version Provides a set of FIFOs (specified by "num_fifos") and one Avalon Slave interface to access them. Data is tagged with one byte specified via register 1. The tag should be written before the data is written and it should be read after the data is read. Each FIFO is allocated 4 word addresses to access it. Processor memory map: read/write address meaning read 0 read from fifo write 0 write word to fifo read 1 bit 9 = overflow-output, bit 8 = underflow-input bits 7-0 = tag byte write 1 write tag byte read 2 number of waiting data items write 2 clear fifo + overflow and underflow bits read 3 number of empty fifo slots write 3 clear fifo + overflow and underflow bits Bug fixes: * in mkAvalonFIFOs_UGFIFOCount - moved fifo.clear from clear method to keep_count rule - the fifo is now cleared when the counter is cleared - seemed to be necessary to help the scheduler otherwise fifo.enq in do_enq never fired despite no_implicit_conditions *****************************************************************************/ package AvalonFIFOs; import GetPut::*; import Vector::*; import ClientServer::*; import Avalon2ClientServer::*; import FIFOF::*; import FIFOLevel::*; import FIFO::*; import Connectable::*; `define fifo_depth 32 // num fifos for single Avalon port version `define num_fifos 4 // num ports for multi-Avalon slave version `define num_ports 3 `define fifo_address_width 2 //`define replication_address_width Log(num_fifos) `define replication_address_width 2 //`define total_address_width (fifo_address_width+replication_address_width); `define total_address_width 4 (* always_enabled, always_ready *) interface AvalonFIFOs_UGFIFOCountIfc#( type element_type ); method Action enq ( element_type sendData ) ; method Action deq () ; method element_type first () ; method Bool notFull ; method Bool notEmpty ; method UInt#(32) count; //TODO: fix this hack... method Action clear; endinterface // like FIFOCountIfc but with ungarded enq and deq module mkAvalonFIFOs_UGFIFOCount#(Integer fifoDepth)(AvalonFIFOs_UGFIFOCountIfc#(element_type)) provisos (Bits#(element_type,element_type_width)); FIFOF#(element_type) fifo <- mkUGSizedFIFOF(fifoDepth); Wire#(Maybe#(element_type)) enq_dw <- mkDWire(tagged Invalid); PulseWire deq_pulse <- mkPulseWire; Reg#(UInt#(32)) counter <- mkReg(0); // hack - fix PulseWire count_down <- mkPulseWire; PulseWire count_up <- mkPulseWire; PulseWire count_zero <- mkPulseWire; Wire#(element_type) first_bw <- mkBypassWire; (* no_implicit_conditions *) rule do_first_bypass (True); // needed? first_bw <= fifo.notEmpty ? fifo.first : unpack(0); endrule (* no_implicit_conditions *) rule keep_count; if(count_zero) begin counter <= 0; fifo.clear; end else if(count_up && !count_down) counter<=counter+1; else if(!count_up && count_down) counter<=counter-1; endrule (* no_implicit_conditions *) rule do_enq (isValid(enq_dw) && fifo.notFull); fifo.enq(fromMaybe(?,enq_dw)); count_up.send; $display("%05d: %m - do_enq fired",$time); endrule rule do_enq_failed (isValid(enq_dw) && !fifo.notFull); $display("%05d: %m - WARNING: fifo enq when full",$time); endrule (* no_implicit_conditions *) rule do_deq (deq_pulse && fifo.notEmpty); fifo.deq; count_down.send; endrule rule do_deq_failed (deq_pulse && !fifo.notEmpty); $display("Warning: fifo deq when full"); endrule method Action enq ( element_type sendData ); enq_dw <= tagged Valid sendData; endmethod method Action deq (); deq_pulse.send; endmethod method element_type first (); return first_bw; endmethod method Bool notFull = fifo.notFull; method Bool notEmpty = fifo.notEmpty; method count; return extend(counter); endmethod method Action clear; count_zero.send; endmethod endmodule typedef UInt#(8) AvalonFIFOs_tagT; typedef struct { AvalonFIFOs_tagT tag; AvalonWordT data; } AvalonFIFOs_tagdataT deriving (Bits); typedef struct { UInt#(`replication_address_width) addr_top; UInt#(`fifo_address_width) addr_bot; } AvalonFIFOs_Addr_SeperatorT deriving (Bits); interface AvalonFIFOsIfc; interface AvalonSlaveIfc#(`total_address_width) avs; endinterface (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) (* doc="************** Simon's comments for Verilog code: **************" *) (* doc="Top level module for Avalon Bridge Example" *) (* doc="Port names should align with SOPC builder's new device requirements" *) (* doc="Need to add the $BLUESPECDIR/Verilog directory to the Libraries in Quartus" *) module mkAvalonFIFOs(AvalonFIFOsIfc); AvalonSlave2ClientIfc#(`total_address_width) slave <- mkAvalonSlave2Client; Vector#(`num_fifos,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) f <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_fifos,Reg#(AvalonFIFOs_tagT)) tag_in <- replicateM(mkReg(0)); Vector#(`num_fifos,Reg#(AvalonFIFOs_tagT)) tag_out <- replicateM(mkReg(0)); Vector#(`num_fifos,Reg#(Bool)) underflow <- replicateM(mkReg(False)); Vector#(`num_fifos,Reg#(Bool)) overflow <- replicateM(mkReg(False)); rule handle_requests; let req <- slave.client.request.get; ReturnedDataT rtn = tagged Invalid; AvalonFIFOs_Addr_SeperatorT a = unpack(pack(req.addr)); case(tuple2(a.addr_bot,req.rw)) tuple2(0, MemRead ) : if(f[a.addr_top].notEmpty) begin let d = f[a.addr_top].first; f[a.addr_top].deq; rtn = tagged Valid d.data; tag_in[a.addr_top] <= d.tag; end else begin rtn = tagged Valid 0; tag_in[a.addr_top] <= 0; underflow[a.addr_top] <= True; end tuple2(0, MemWrite) : if(f[a.addr_top].notFull) f[a.addr_top].enq(AvalonFIFOs_tagdataT{tag:tag_out[a.addr_top], data:req.data}); else overflow[a.addr_top] <= True; tuple2(1, MemRead ) : rtn = tagged Valid extend(unpack({pack(overflow[a.addr_top]),pack(underflow[a.addr_top]),pack(tag_in[a.addr_top])})); tuple2(1, MemWrite) : tag_out[a.addr_top] <= truncate(req.data); tuple2(2, MemRead ) : rtn = tagged Valid extend(f[a.addr_top].count); tuple2(2, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; f[a.addr_top].clear; end tuple2(3, MemRead ) : rtn = tagged Valid extend(`fifo_depth-f[a.addr_top].count); tuple2(3, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; f[a.addr_top].clear; end endcase slave.client.response.put(rtn); endrule interface avs=slave.avs; endmodule /***************************************************************************** AvalonFIFOMultiPort =================== Simon Moore, Dec 2009 Provides N Avalon Slaves and point-to-point FIFO communication between these ports (i.e. grows O(N^2)). It is constructed from N x AvalonFIFOOnePort Port P has N output ports and N input ports, i.e. there is one port which allows it to communicate with itself Use same address map as AvalonFIFOs. Processor memory map (word addressed): read/write address meaning read 0 read from fifo write 0 write word to fifo read 1 bit 9 = overflow-output, bit 8 = underflow-input bits 7-0 = tag byte write 1 write tag byte read 2 number of waiting data items write 2 clear fifo + overflow and underflow bits read 3 number of empty fifo slots write 3 clear fifo + overflow and underflow bits *****************************************************************************/ interface AvalonFIFOOnePortIfc; interface AvalonSlaveIfc#(`total_address_width) avs; interface Vector#(`num_ports,Client#(AvalonFIFOs_tagdataT,AvalonFIFOs_tagdataT)) chan; endinterface module mkAvalonFIFOOnePort(AvalonFIFOOnePortIfc); AvalonSlave2ClientIfc#(`total_address_width) slave <- mkAvalonSlave2Client; // TODO - use just one long fifo and token based flow control? // - N.B. require "number of empty fifo slots" for sender Vector#(`num_ports,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) fin <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_ports,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) fout <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_ports,Reg#(AvalonFIFOs_tagT)) tag_in <- replicateM(mkReg(0)); Vector#(`num_ports,Reg#(AvalonFIFOs_tagT)) tag_out <- replicateM(mkReg(0)); Vector#(`num_ports,Reg#(Bool)) underflow <- replicateM(mkReg(False)); Vector#(`num_ports,Reg#(Bool)) overflow <- replicateM(mkReg(False)); rule handle_requests; let req <- slave.client.request.get; ReturnedDataT rtn = tagged Invalid; AvalonFIFOs_Addr_SeperatorT a = unpack(pack(req.addr)); case(tuple2(a.addr_bot,req.rw)) tuple2(0, MemRead ) : if(fin[a.addr_top].notEmpty) begin let d = fin[a.addr_top].first; fin[a.addr_top].deq; rtn = tagged Valid d.data; tag_in[a.addr_top] <= d.tag; end else begin rtn = tagged Valid 0; tag_in[a.addr_top] <= 0; underflow[a.addr_top] <= True; end tuple2(0, MemWrite) : if(fout[a.addr_top].notFull) fout[a.addr_top].enq(AvalonFIFOs_tagdataT{tag:tag_out[a.addr_top], data:req.data}); else overflow[a.addr_top] <= True; tuple2(1, MemRead ) : rtn = tagged Valid extend(unpack({pack(overflow[a.addr_top]),pack(underflow[a.addr_top]),pack(tag_in[a.addr_top])})); tuple2(1, MemWrite) : tag_out[a.addr_top] <= truncate(req.data); tuple2(2, MemRead ) : rtn = tagged Valid extend(fin[a.addr_top].count); tuple2(2, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; fout[a.addr_top].clear; end tuple2(3, MemRead ) : rtn = tagged Valid extend(`fifo_depth-fout[a.addr_top].count); tuple2(3, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; fin[a.addr_top].clear; fout[a.addr_top].clear; end endcase slave.client.response.put(rtn); endrule // this is a bit of a hack to provide get and put interfaces to fifoin and fifoout Vector#(`num_ports,FIFO#(AvalonFIFOs_tagdataT)) bout <- replicateM(mkFIFO); Vector#(`num_ports,FIFO#(AvalonFIFOs_tagdataT)) bin <- replicateM(mkFIFO); for(Integer p=0; p<`num_ports; p=p+1) begin rule bin_to_fin (fin[p].notFull); fin[p].enq(bin[p].first); bin[p].deq; endrule rule fout_to_bout(fout[p].notEmpty); bout[p].enq(fout[p].first); fout[p].deq; endrule end Vector#(`num_ports,Client#(AvalonFIFOs_tagdataT,AvalonFIFOs_tagdataT)) c = newVector; for(Integer p=0; p<`num_ports; p=p+1) c[p] = Client{request: toGet(bout[p]), response: toPut(bin[p])}; interface chan=c; interface avs=slave.avs; endmodule interface AvalonFIFOMultiPortIfc; interface Vector#(`num_ports,AvalonSlaveIfc#(`total_address_width)) avs; endinterface (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) (* doc="************** Simon's comments for Verilog code: **************" *) (* doc="Top level module for Avalon Bridge Example" *) (* doc="Port names should align with SOPC builder's new device requirements" *) (* doc="Need to add the $BLUESPECDIR/Verilog directory to the Libraries in Quartus" *) module mkAvalonFIFOMultiPort(AvalonFIFOMultiPortIfc); Vector#(`num_ports,AvalonFIFOOnePortIfc) ports <- replicateM(mkAvalonFIFOOnePort); Vector#(`num_ports,AvalonSlaveIfc#(`total_address_width)) slaves; for(Integer src=0; src<`num_ports; src=src+1) for(Integer dest=0; dest<`num_ports; dest=dest+1) mkConnection(ports[src].chan[dest].request.get,ports[dest].chan[src].response.put); for(Integer j=0; j<`num_ports; j=j+1) slaves[j] = ports[j].avs; interface avs = slaves; endmodule endpackage
Link to the AvalonFIFOs.bsv source