Commit 05a118d9 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

working with cocotb simulation

parent ca6e4334
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -7,8 +7,9 @@ cocotb_@_CocotbTESTCASE=run_test<-@\#\#@->
cocotb_@_GTKWaveSavFile=x393_cocotb_02.sav
cocotb_@_GrepFindErr=error|ERROR
cocotb_@_GrepFindErrWarn=error|warning|ERROR|WARNING
cocotb_@_PatternInfo=.*[\\s.](\\w*\\.py)\:([0-9]+)\\s*\\S*\\s*\\S*\\s*(.*)
cocotb_@_SaveLogsPreprocessor=true
cocotb_@_SaveLogsSimulator=true
com.elphel.store.context.cocotb=cocotb_@_CocotbExtraFiles<-@\#\#@->cocotb_@_CocotbMODULE<-@\#\#@->cocotb_@_GTKWaveSavFile<-@\#\#@->cocotb_@_CocotbIncludeDir<-@\#\#@->cocotb_@_CocotbDutTopFile<-@\#\#@->cocotb_@_CocotbTESTCASE<-@\#\#@->cocotb_@_GrepFindErrWarn<-@\#\#@->cocotb_@_COCOTB_DEBUG<-@\#\#@->cocotb_@_SaveLogsPreprocessor<-@\#\#@->cocotb_@_SaveLogsSimulator<-@\#\#@->cocotb_@_GrepFindErr<-@\#\#@->
com.elphel.store.context.cocotb=cocotb_@_CocotbExtraFiles<-@\#\#@->cocotb_@_CocotbMODULE<-@\#\#@->cocotb_@_GTKWaveSavFile<-@\#\#@->cocotb_@_CocotbIncludeDir<-@\#\#@->cocotb_@_CocotbDutTopFile<-@\#\#@->cocotb_@_CocotbTESTCASE<-@\#\#@->cocotb_@_GrepFindErrWarn<-@\#\#@->cocotb_@_SaveLogsPreprocessor<-@\#\#@->cocotb_@_SaveLogsSimulator<-@\#\#@->cocotb_@_GrepFindErr<-@\#\#@->cocotb_@_PatternInfo<-@\#\#@->cocotb_@_COCOTB_DEBUG<-@\#\#@->
com.elphel.store.version.cocotb=0.8
eclipse.preferences.version=1
+11 −8
Original line number Diff line number Diff line
@@ -170,6 +170,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
//`endif        
    reg         en;      // enable mux
    reg         en_d;    // or use it to reset all channels?
    wire        en_nrst = en && ! hrst; // when hclk is not yet available 
    reg   [3:0] en_chn;  // per-channel enable 
    
    wire [31:0] cmd_data;
@@ -272,6 +273,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
    wire [1:0] want_wleft32 = (|items_left[7:2])? 2'b11 : items_left[1:0]; // want to set wleft[3:2] if not roll-over (actually "3" means 2)

    wire rollover_limited_w = max_wlen[1:0] < want_wleft32;
    wire afi_wvalid_w;
    

    assign cmd_we_status_w = cmd_we && ((cmd_a & 'hc) ==       CMPRS_AFIMUX_STATUS_CNTRL);    
@@ -280,7 +282,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
    assign cmd_we_sa_len_w = cmd_we && ((cmd_a & 'h8) ==       CMPRS_AFIMUX_SA_LEN);
    assign cmd_we_en_w =     cmd_we && (cmd_a ==               CMPRS_AFIMUX_EN);    
    assign cmd_we_rst_w =    cmd_we && (cmd_a ==               CMPRS_AFIMUX_RST);    
    
    assign afi_wvalid =      afi_wvalid_w && !hrst;
    
    
    // use last_chunk_w to apply a special id to waddr and wdata and watch for it during readout
@@ -300,7 +302,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
    
    assign afi_awaddr =  {chunk_addr,5'b0};
    assign afi_awid =    afi_awid_r; //  {1'b0,wleft[3:2],last_burst_in_frame,cur_chn}; 
    assign afi_awvalid = awvalid[1];
    assign afi_awvalid = awvalid[1] && !hrst;
//    assign afi_awlen = {wleft[3:2],2'b11};
    assign afi_wdata = wdata;
//    assign afi_bready = 1'b1; // always ready
@@ -371,7 +373,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),

    
    always @ (posedge hclk) begin
        en_d <= en;
        en_d <= en && !hrst;
    
        ready_to_start <= en && // ready to strta a burst
                          !afi_wacount[5] && !(&afi_wacount[4:1]) &&  // >=2 free 
@@ -472,7 +474,8 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
        
        else if (wleft != 0) wleft <= wleft - 1;

        if      (!en)        wvalid <= 0;
 //       if      (!en)        wvalid <= 0;
        if      (!en_nrst)   wvalid <= 0;
        else if (pre_busy_w) wvalid <= 1;
        else if (wlast)      wvalid <= 0; // should be after pre_busy_w as both can happen simultaneously

@@ -489,8 +492,8 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
        
        
        
        
        awvalid <= {awvalid[0],pre_busy_w}; // no need to wait for afi_awready, will use fifo levels to enable pre_busy_w
        if (hrst) awvalid <= 0; 
        else      awvalid <= {awvalid[0],pre_busy_w}; // no need to wait for afi_awready, will use fifo levels to enable pre_busy_w
        
        if (pre_busy_w)  begin
            cur_chn <= winner2;
@@ -523,7 +526,7 @@ each group of 4 bits per channel : bits [1:0] - select, bit[2] - sset (0 - nop),
        .rst       (!en),  // input
        .dly       (AFI_MUX_BUF_LATENCY), // input[3:0] will delay by AFI_MUX_BUF_LATENCY+1 (normally 3) 
        .din       ({    wvalid,     wlast}), // , afi_awid_r}), // afi_awid}), // input[0:0] 
        .dout      ({afi_wvalid, afi_wlast}) //, afi_wid})     // output[0:0] 
        .dout      ({afi_wvalid_w, afi_wlast}) //, afi_wid})     // output[0:0] 
    );
    localparam [3:0] AFI_MUX_BUF_LATENCYM1 = AFI_MUX_BUF_LATENCY - 1;
    dly_16 #(
+18 −1
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@ class SocketCommand():
        return self.command == "stop" 
    def getWrite(self):
        return self.arguments if self.command == "write" else None
    def getWait(self):
        return self.arguments if self.command == "wait" else None
    def getFlush(self):
        return self.command == "flush"
    def getRead(self):
        return self.arguments if self.command == "read" else None
    def setStart(self):
@@ -48,6 +52,11 @@ class SocketCommand():
    def setWrite(self,arguments):
        self.command = "write"
        self.arguments=arguments
    def setWait(self,arguments): # wait irq mask, timeout (ns)
        self.command = "wait"
        self.arguments=arguments
    def setFlush(self):         #flush memory file (use when sync_for_*
        self.command = "flush"
    def setRead(self,arguments):
        self.command = "read"
        self.arguments=arguments
@@ -88,7 +97,15 @@ class x393Client():
    def write(self, address, data):
        self.cmd.setWrite([address,data])
        rslt = self.communicate(self.cmd.toJSON())
        print("write->",rslt)
#        print("write->",rslt)
    def waitIrq(self, irqMask,wait_ns):
        self.cmd.setWait([irqMask,wait_ns])
        rslt = self.communicate(self.cmd.toJSON())
#        print("waitIrq->",rslt)
    def flush(self):
        self.cmd.setFlush()
#        print("flush->",self.communicate(self.cmd.toJSON()))

    def read(self, address):
        self.cmd.setRead(address)
#        print("read->args",self.cmd.getArgs())
+493 −11

File changed.

Preview size limit exceeded, changes collapsed.

+193 −46
Original line number Diff line number Diff line
@@ -28,43 +28,126 @@ import select
from socket_command import SocketCommand

from cocotb.triggers import Timer
from x393interfaces import MAXIGPMaster
from x393interfaces import MAXIGPMaster, PSBus, SAXIRdSim, SAXIWrSim
from cocotb.drivers import BitDriver
from cocotb.triggers import Timer, RisingEdge, ReadOnly
from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess

import logging
import re
import struct
def hex_list(lst, max_items=0, frmt="0x%08x"):
    if (max_items == 0) or (len(lst) <= max_items):
        hs="["
        for d in lst:
            hs+=frmt%(d)+", "
        return hs[:-2]+"]"    
    hs = "%d ["%len(lst)
    fi = max_items-1 if max_items > 1 else max_items
    for d in lst[:fi]:
        hs+=frmt%(d)+", "
    hs += "..."
    if fi < max_items:
        hs += " "+frmt%(d)
    return hs+"]"    
    
class X393_cocotb_server(object):
    INTR_ADDRESS = 0xfffffff0 #temporary address
    INTM_ADDRESS = 0xfffffff4 #temporary address
    RESERVED = (INTR_ADDRESS,INTM_ADDRESS)
    writeIDMask = (1 <<12) -1
    readIDMask = (1 <<12) -1
    started=False
    def __init__(self, dut, port, host, mempath="memfile", autoflush=True): # , debug=False):

    int_mask = 0 # all disabled
    def __init__(self, dut, port, host, mempath=None, autoflush=True): # , debug=False):
        self.ACLK_FREQ=50000000 # 50 MHz
        debug = os.getenv('COCOTB_DEBUG') # None/1
        if mempath is None:
            mempath =    os.getenv('SIMULATION_PATH')+"/"+"memfile"
        self.mempath =   mempath
        self.memlow =    0
        self.memhigh =   0x40000000
        self.autoflush = autoflush
        self.cmd=        SocketCommand()
        self.dut =       dut
        #Open file to use as system memory
        try:
            self._memfile=open(mempath, 'r+') #keep old file if it exists already
        except:    
            self._memfile=open(mempath, 'w+') #create a new file if it does not exist
            self.dut._log.info ("Created a new 'memory' file %s"%(mempath)) #
        #Extend to full size
        self._memfile.seek(self.memhigh-1)
        readOK=False
        try:
            readOK = len(self._memfile.read(1))>0
            self.dut._log.info ("Read from 0x%08x"%(self.memhigh-1)) #
            
        except:
            pass
        if not readOK:
            self._memfile.seek(self.memhigh-1)
            self._memfile.write(chr(0))
            self._memfile.flush()
            self.dut._log.info("Wrote to 0x%08x to extend file to full size"%(self.memhigh-1)) #
        
        #initialize MAXIGP0 interface (main control/status registers, TODO: add MAXIGP1 for SATA)
        self.maxigp0 = MAXIGPMaster(entity=dut, name="dutm0", clock=dut.dutm0_aclk, rdlag=0, blag=0)
        self.maxigp0 = MAXIGPMaster(entity =   dut,
                                    name =     "dutm0",
                                    clock =    dut.dutm0_aclk,
                                    rdlag =    0,
                                    blag=0)
        self.writeID=0
        self.readID=0
        #initialize Zynq register access, has methods write_reg(a,d) and read_reg(a)
        self.ps_sbus = PSBus(entity=dut, name="ps_sbus", clock=dut.ps_sbus_clk)
        self.ps_sbus = PSBus       (entity =     dut,
                                    name =       "ps_sbus",
                                    clock =      dut.ps_sbus_clk)
        #Bus masters (communicated over mempath file
        #Membridge to FPGA
        self.saxihp0r = SAXIRdSim(entity=dut, name="saxihp0", clock=dut.axi_hclk, mempath = mempath, memhigh=0x40000000, data_bytes=8)
        self.saxihp0r = SAXIRdSim  (entity =     dut,
                                    name =       "saxihp0",
                                    clock =      dut.axi_hclk,
                                    mempath =    self.mempath,
                                    memhigh =    self.memhigh,
                                    data_bytes = 8)
        #Membridge from FPGA
        self.saxihp0w = SAXIWrSim(entity=dut, name="saxihp0", clock=dut.axi_hclk, mempath = mempath, memhigh=0x40000000, data_bytes=8,
                                  autoflush = autoflush, blatency=5)
        self.saxihp0w = SAXIWrSim  (entity =     dut,
                                    name =       "saxihp0",
                                    clock =      dut.axi_hclk,
                                    mempath =    self.mempath,
                                    memhigh =    self.memhigh,
                                    data_bytes = 8,
                                    autoflush =  self.autoflush,
                                    blatency =   5)
        #Compressors from FPGA
        self.saxihp1w = SAXIWrSim(entity=dut, name="saxihp1", clock=dut.axi_hclk, mempath = mempath, memhigh=0x40000000, data_bytes=8,
                                  autoflush = autoflush, blatency=5)
        self.saxihp1w = SAXIWrSim  (entity =     dut,
                                    name =       "saxihp1",
                                    clock =      dut.axi_hclk,
                                    mempath =    self.mempath,
                                    memhigh =    self.memhigh,
                                    data_bytes = 8,
                                    autoflush =  self.autoflush,
                                    blatency =   5)
        #histograms from FPGA
        self.saxigp0 =   SAXIWrSim(entity=dut, name="saxigp0", clock=dut.saxigp0, mempath = mempath, memhigh=0x40000000, data_bytes=4,
                                  autoflush = autoflush, blatency=5)
        self.saxigp0 =   SAXIWrSim (entity =     dut,
                                    name =       "saxigp0",
                                    clock =      dut.saxi0_aclk,
                                    mempath =    self.mempath,
                                    memhigh =    self.memhigh,
                                    data_bytes = 4,
                                    autoflush =  self.autoflush,
                                    blatency =   5)
        
        level = logging.DEBUG if debug else logging.WARNING
        self.dut._log.info('Set debug level '+str(level)+", debug="+str(debug))
        
        self.maxigp0.log.setLevel(level)
        self.ps_sbus.log.setLevel(level)
        self.saxihp0r.log.setLevel(level)
        self.saxihp0w.log.setLevel(level)
        self.saxihp1w.log.setLevel(level)
        self.saxigp0.log.setLevel(level)
        
        #Initialize socket
        self.PORT = port
        self.HOST = host   # Symbolic name meaning all available interfaces
@@ -76,11 +159,10 @@ class X393_cocotb_server(object):
            self.socket_conn.listen(1) # just a single request (may increase to 5 (backlog)
            self.dut._log.info ('Socket now listening to a single request on port %d: send command, receive response, close'%(self.PORT))
        except socket.error as msg:
            self.dut._log.info ("Maybe you need to run 'killall vvp' to close previously opened socket?" )
            self.logErrorTerminate('Bind failed. Error Code : %s Message %s'%( str(msg[0]),msg[1]))
            
        
            
        
    def logErrorTerminate(self, msg):
        self.dut._log.error(msg)
        cocotb.regression.tear_down()
@@ -116,15 +198,13 @@ class X393_cocotb_server(object):

            while self.dut.reset_out.value.get_binstr() != "1":
                yield Timer(10000)
        
            while self.dut.reset_out.value:
                yield Timer(10000)
            # Launch all bus masters (no need to join ever, preserving just in case
            self.self.saxihp0r_thread = cocotb.fork(saxihp0r.saxi_rd_run)    
            self.self.saxihp0w_thread = cocotb.fork(saxihp0w.saxi_wr_run)    
            self.self.saxihp1w_thread = cocotb.fork(saxihp1w.saxi_wr_run)    
            self.self.saxihp2w_thread = cocotb.fork(saxihp2w.saxi_wr_run)    

            self.saxihp0r_thread = cocotb.fork(self.saxihp0r.saxi_rd_run())    
            self.saxihp0w_thread = cocotb.fork(self.saxihp0w.saxi_wr_run())    
            self.saxihp1w_thread = cocotb.fork(self.saxihp1w.saxi_wr_run())    
            self.saxigp0_thread =  cocotb.fork(self.saxigp0.saxi_wr_run())    
            self.soc_conn.send(self.cmd.toJSON(0)+"\n")
            self.dut._log.debug('Sent 0 to the socket')
            started=True
@@ -143,9 +223,20 @@ class X393_cocotb_server(object):
        #For now write - one at a time, TODO: a) consolidate, b) decode address (some will be just a disk file)
        elif self.cmd.getWrite():
            ad = self.cmd.getWrite()
            self.dut._log.info('Received WRITE, 0x%0x: %s'%(ad[0],str(ad[1])))
            if ad[0] < 0x40000000:
                pass
            self.dut._log.debug('Received WRITE, 0x%0x: %s'%(ad[0],hex_list(ad[1])))
            if ad[0]in self.RESERVED:
                if ad[0] == self.INTM_ADDRESS:
                    self.int_mask = ad[1][0]
                rslt = 0 
            elif (ad[0] >= self.memlow) and  (ad[0] < self.memhigh):
                addr = ad[0]
                self._memfile.seek(addr)
                for data in ad[1]: # currently only single word is supported
                    sdata=struct.pack("<L",data) # little-endian, u32
                    self._memfile.write(sdata)
                    self.dut._log.debug("Written 'system memory': 0x%08x => 0x%08x"%(data,addr))
                    addr += 4
                rslt = 0 
            elif(ad[0] >= 0x40000000) and (ad[0] < 0x80000000):
                rslt = yield self.maxigp0.axi_write(address =     ad[0],
                                                value =           ad[1],
@@ -155,42 +246,98 @@ class X393_cocotb_server(object):
                                                burst =           1,
                                                address_latency = 0,
                                                data_latency =    0)
                self.dut._log.info('maxigp0.axi_write yielded %s'%(str(rslt)))
                self.dut._log.debug('maxigp0.axi_write yielded %s'%(str(rslt)))
                self.writeID = (self.writeID+1) & self.writeIDMask
            elif ad[0] >= 0xc0000000:
            elif (ad[0] >= 0xc0000000) and (ad[0] < 0xfffffffc):
                self.ps_sbus.write_reg(ad[0],ad[1][0])
                rslt = 0 
            else:
                self.dut._log.info('Write address 0x%08x is outside of maxgp0, not yet supported'%(ad[0]))
                rslt = 0
            self.dut._log.info('WRITE 0x%08x <= %s'%(ad[0],hex_list(ad[1], max_items = 4)))
            self.soc_conn.send(self.cmd.toJSON(rslt)+"\n")
            self.dut._log.debug('Sent rslt to the socket')
        elif self.cmd.getRead():
            a = self.cmd.getRead()
            if a < 0x40000000:
                pass
            if(a >= 0x40000000) and (a < 0x80000000):
                dval = yield  self.maxigp0.axi_read(address =     a,
            ad = self.cmd.getRead()
            self.dut._log.debug(str(ad))
            if not isinstance(ad,(list,tuple)):
                ad=(ad,1)
            elif len(ad) < 2:
                ad=(ad[0],1)
            self.dut._log.debug(str(ad))
            if ad[0]in self.RESERVED:
                if ad[0] == self.INTR_ADDRESS:
                    try:
                        dval=[self.dut.irq_r.value.integer]
                    except:
                        bv = self.dut.irq_r.value
                        bv.binstr = re.sub("[^1]","0",bv.binstr)
                        dval=[bv.integer]
                elif ad[0] == self.INTM_ADDRESS:
                    dval = [self.int_mask]
                else:
                    dval = [0]    
            elif (ad[0] >= self.memlow) and  (ad[0] < self.memhigh):
                addr = ad[0]
                self._memfile.seek(addr)
                self.dut._log.debug("read length="+str(len(self._memfile.read(4*ad[1]))))
                
                self._memfile.seek(addr)
                self.dut._log.debug(str(ad))
                dval = list(struct.unpack("<"+"L"*ad[1],self._memfile.read(4*ad[1])))
                msg="'Written 'system memory: 0x%08x => "%(addr)
                for d in dval:
                    msg += "0x%08x "%(d)
                self.dut._log.debug(msg)
                
            elif(ad[0] >= 0x40000000) and (ad[0] < 0x80000000):
                dval = yield  self.maxigp0.axi_read(address =     ad[0],
                                               id =               self.readID,
                                                dlen =            1,
                                                dlen =            ad[1],
                                                dsize =           2,
                                                address_latency = 0,
                                                data_latency =    0 )
                self.dut._log.info("axi_read returned => " +str(dval))
                self.dut._log.debug("axi_read returned 0x%08x => %s"%(ad[0],hex_list(dval, max_items = 4)))
                self.readID = (self.readID+1) & self.readIDMask
            elif ad[0] >= 0xc0000000:
            elif (ad[0]>= 0xc0000000) and (ad[0] < 0xfffffffc):
                dval = yield  self.ps_sbus.read_reg(ad[0])
            else:
                self.dut._log.info('Read address 0x%08x is outside of maxgp0, not yet supported'%(a))
                self.dut._log.info('Read address 0x%08x is outside of maxgp0, not yet supported'%(ad[0]))
                dval = [0]    
            self.soc_conn.send(self.cmd.toJSON(dval)+"\n")
            self.dut._log.debug('Sent dval to the socket')
            self.dut._log.info("READ 0x%08x =>%s"%(ad[0],hex_list(dval, max_items = 4)))
        elif self.cmd.getFlush():
            self.dut._log.info('Received flush')
            self.flush_all()
            self.soc_conn.send(self.cmd.toJSON(0)+"\n")
            self.dut._log.debug('Sent 0 to the socket')
            
    def flush_all(self):
        self.self.saxihp0w.flush()
        self.self.saxihp1w.flush()
        self.self.saxihp2w.flush()
        
        elif self.cmd.getWait():
#self.MAXIGP0_CLK_FREQ            
            int_dly = self.cmd.getWait()
            self.int_mask = int_dly[0]
            num_clk= (int_dly[1] * self.ACLK_FREQ) // 1000000000
            self.dut._log.info('Received WAIT, interrupt mask = 0x%0x, timeout = %d ns, %d clocks'%(self.int_mask,int_dly[1], num_clk))
            n = 0
            for _ in range(num_clk):
                yield RisingEdge(self.dut.dutm0_aclk)
                try:
                    irq_r=self.dut.irq_r.value.integer
                except:
                    bv = self.dut.irq_r.value
                    bv.binstr = re.sub("[^1]","0",bv.binstr)
                    irq_r=bv.integer
                if (self.int_mask & irq_r):
                    break
                n += 1
            self.soc_conn.send(self.cmd.toJSON(n)+"\n")
            self.dut._log.debug('Sent %d to the socket'%(n))
            self.dut._log.info(' WAIT over, passed %d ns'%((n * 1000000000)//self.ACLK_FREQ))
        else:
            self.dut._log.warning('Received unknown command: '+str(self.cmd))
            self.soc_conn.send(self.cmd.toJSON(1)+"\n")
            self.dut._log.debug('Sent 1 to the socket')
            
def convert_string(txt):
    number=0
Loading