/cosim_bfm_library

HW-SW Co-Simulation Library for AMBA AXI BFM using DPI/VPI

Primary LanguageCOtherNOASSERTION

HW-SW Co-Simulation Library for AMBA AXI BFM using DPI/VPI

Cosim BFM library is a package to provide HW-SW co-simulation between the HDL (Hardware Description Language) simulator and the host program, where BFM (Bus Functional Model or Bus Functional Module) generates bus transaction by interacting with the host program in C or Python.

Co-simulation using BFM
Co-simulation using BFM

Table of contents

Click to expand table of contents
  1. Getting started
    1.1 Getting started using DPI
    1.2 Getting started using VPI
  2. Installation
    2.1 DPI for Xilinx xsim
    2.2 VPI for Icarus Verilog
  3. Software side
    3.1 C API for software side
    3.2 Python functions for software side
  4. Hardware side
    4.1 DPI functions for hardware side
    4.2 VPI functions for hardware side
  5. Troubleshooting
  6. Where to get more information
  7. Other things

License

This is licensed with the 2-clause BSD license to make the program and library useful in open and closed source products independent of their licensing scheme.

Prerequisites

This program requires followings.

  • Shell: Bash
  • GNU GCC: C compiler
  • HDL simulator: Xilinx Vivado simulator or icarus Verilog
  • Python3 if Python interface is required

1. Getting started

Following picture shows a simple setup for testing memory through AMBA AXI bus, where BFM generates write and read bus transactions under the control of the C or Python program.

Co-simulation example
Co-simulation example

Make sure that following two environment variables should be defined properly.

  • COSIM_HOME environment variable is set to the directory where co-simulation library (libcosim_bfm.a or libcosim_bfm.so) is installed.
  • PYTHONPATH environment variable is set to the directory where co-simulation Python code (cosim_bfm.py) resides.

Add following code in the bash startup file, say .bashrc and run 'set_cosim' before you use this library, where COSIM_HOME should be the directory path this library resides.

Click to expand example of .bashrc
set_cosim() {
    export COSIM_HOME=/home/adki/work/cosim_library
    if [ -n "${PYTHONPATH}" ]; then
      export PYTHONPATH=$COSIM_HOME/include/python:$PYTHONPATH
    else
      export PYTHONPATH=$COSIM_HOME/include/python
    fi
}

1.1 Getting started using DPI

Xilinx Vivado simulator should be installed and available.

Click to expand

1.1.1 preparing library

  1. go to 'lib_bfm' directory
    $ cd lib_bfm
  2. compile and install
    $ make -f Makefile.xsim cleanup
    $ make -f Makefile.xsim
    $ make -f Makefile.xsim install
    . It should generate 'include' and 'lib/xsim' directories.

1.1.2 running co-simulation

  1. go to example directory
    $ cd verification/test_axi_dpi_vpi
  2. prepare two command windows and do as follows
    HW-side SW-side
    $ cd hw/sim/xsim $ cd sw
    $ make $ make
    $ make run

or 4) simply run as follows
$ make run_dpi

1.1.2 running co-simulation through Python

  1. go to example directory
    $ cd verification/test_axi_dpi_vpi
  2. prepare two command windows and do as follows (use Python3)
    HW-side Python-side
    $ cd hw/sim/xsim $ cd python
    $ make $ make run

or 4) simply run as follows
$ make run_dpi

Sometimes there may occur "ERROR: data buffer size mis-match" at the begining of co-simulation due to un-expected messages. To deal with this, terminate all related program and re-invoke the program

1.2 Getting started using VPI

Icarus Verilog simulator should be installed and available.

Click to expand

1.2.1 preparing library

  1. go to 'lib_bfm' directory
    $ cd lib_bfm
  2. compile and install
    $ make -f Makefile.iverilog cleanup
    $ make -f Makefile.iverilog
    $ make -f Makefile.iverilog install
    . It should generate 'include' and 'lib/iverilog' directories.

1.2.1 running co-simulation

  1. go to example directory
    $ cd verification/test_axi_dpi_vpi
  2. prepare two command windows and do as follows
    HW-side SW-side
    $ cd hw/sim/xsim $ cd sw
    $ make $ make
    $ make SIMULATOR=iverilog run

or 4) simply run as follows
$ make run_vpi

1.2.2 running co-simulation through Python

  1. go to example directory
    $ cd verification/test_axi_dpi_vpi
  2. prepare two command windows and do as follows (use Python3)
    HW-side Python-side
    $ cd hw/sim/xsim $ cd python
    $ make $ make SIMULATOR=iverilog run

or 4) simply run as follows
$ make run_vpi

Sometimes there may occur "ERROR: data buffer size mis-match" at the begining of co-simulation due to un-expected messages. To deal with this, terminate all related program and re-invoke the program


2. Installation

Installation prepares followings.

  • For host program
    • cosim_bfm_api.h
    • libcosim_bfm.a and libcosim_bfm.so
  • For HDL simulator of DPI
    • cosim_dpi_bfm.a and cosim_dpi_bfm.so
    • cosim_bfm_axi_dpi.sv
  • For HDL simulator of VPI
    • cosim_vpi_bfm.vpi
    • cosim_bfm_axi_vpi.v

2.1 DPI for Xilinx Xsim

Xilinx Vivado simulator should be installed and available.

  1. go to 'lib_bfm' directory
    $ cd lib_bfm
  2. compile and install
    $ make -f Makefile.xsim cleanup
    $ make -f Makefile.xsim
    $ make -f Makefile.xsim install DIR_INSTALL=path
    . It should generate 'include' and 'lib/xsim' directories specified by DIR_INSTALL=path.
    . If DIR_INSTALL=path is not given, .. by default.

2.2 VPI for Icarus Verilog

Icarus Verilog simulator should be installed and available.

  1. go to 'lib_bfm' directory
    $ cd lib_bfm
  2. compile and install
    $ make -f Makefile.iverilog cleanup
    $ make -f Makefile.iverilog
    $ make -f Makefile.iverilog install DIR_INSTALL=path
    . It should generate 'include' and 'lib/iverilog' directories specified by DIR_INSTALL=path.
    . If DIR_INSTALL=path is not given, .. by default.

3. Software side

This host program sends/receives pre-defined packet to/form the HDL simulator over IPC channel.

Click to see packet format
typedef struct {
  unsigned int cmd_type;   // RD-REQ(1), WR-REQ(2), RD-RSP(5), WR-RSP(6), TERM-REQ(8)
  unsigned int cmd_size;   // num of bytes in a beat
  unsigned int cmd_length; // num of beats, i.e., burst length
  unsigned int cmd_ack;    // ERR(0), OK(1)
  unsigned int attr;       // user-specified attribute
  uint32_t     trans_id;   // transaction identification (for multiple outstanding case)
  uint32_t     addr;
  uint8_t      data[COSIM_DATA_BNUM]; // byte-stream up to 4*256 bytes
} bfm_packet_t;

3.1 C API for software side

Following shows a minimum code to deal with co-simulation and there should be a corresponding hardware simulator.

#include "cosim_bfm_api.h"

int main(void) {
    int cid = 0;
    bfm_open   (cid);
    bfm_barrier(cid);
    ... your code using bfm_write() and bfm_read() ...
    bfm_close  (cid);
    return 0;
}

3.1.1 Co-simulation handling routines for C/C++

  • bfm_open() tries to create and open communication channel to the hardware simulator, where cid specifies channel identification and 0 by default. It returns 0 on success or negative number on failure.
int bfm_open(int cid);
  • bfm_close() closes the communication channel with the channel cid. It returns 0 on success or negative number on failure.
int bfm_close(int cid);
  • bfm_barrier() waits for joining the hardware simulator. It returns 0 on success or negative number on failure.
int bfm_barrier(int cid);
  • bfm_set_verbose() sets verbosity level, where `level' is 0 by default to depress message. It returns 0 on success or negative number on failure.
int bfm_set_verbose(int level);
  • bfm_get_verbose() returns current verbosity level.
int bfm_get_verbose();

3.1.2 Bus transaction routines for C/C++

  • bfm_write() makes HW BFM generates a burst write transaction.
    • addr for the starting address that should be aligned with the sz.
    • data for the buffer containing byte-stream data to be written, where the size of data buffer should be sz x length.
    • sz for the number of bytes to be written at a each transaction and can be 1, 2, and 4.
    • returns 0 on success, otherwise negative number.
int bfm_write( uint32_t     addr
             , uint8_t     *data
             , unsigned int sz
             , unsigned int length);
  • bfm_read() makes HW BFM generates a burst read transaction.
    • addr for the starting address that should be aligned with the sz.
    • data for the buffer to be contain byte-stream data after read, where the size of data buffer should be sz x length.
    • sz for the number of bytes to be written at a each transaction and can be 1, 2, and 4.
    • returns 0 on success, otherwise negative number.
int bfm_read ( uint32_t     addr
             , uint8_t     *data
             , unsigned int sz
             , unsigned int length);

3.2. Python functions for software side

Python3 should be used.

Following shows a minimum code to deal with co-simulation and there should be a corresponding hardware simulator. Environment variable PYTHONPATH should contain the directory path that cosim_bfm.py resides.

#!/usr/bin/env python3

import cosim_bfm as cosim

simulator = xsim # can be "iverilog"
cid = 0

cosim.LoadCosimLib(simulator)
cosim.bfm_open(cid)
cosim.bfm_barrier(cid)

... your code using bfm_write() and bfm_read() ...

cosim.bfm_close(cid)

3.2.1 Co-simulation handling routines for Python3

  • LoadCosimLib() loads C shared library from COSIM_HOME environment variable, where simulator specifies which HDL simulator and xsim or iverilog is supported for this version.
LoadCosimLib( simulator, rigor=False, verbose=False)
  • bfm_open() tries to create and open communication channel to the hardware simulator, where cid specifies channel identification and 0 by default. It returns 0 on success or negative number on failure.
bfm_open( cid=0, rigor=False, verbose=False )
  • bfm_close() closes the communication channel with the channel cid. It returns 0 on success or negative number on failure.
bfm_close( cid=0, rigor=False, verbose=False )
  • bfm_barrier() waits for joining the hardware simulator. It returns 0 on success or negative number on failure.
bfm_barrier( cid=0, rigor=False, verbose=False )
  • bfm_set_verbose() sets verbosity level, where `level' is 0 by default to depress message. It returns 0 on success or negative number on failure.
bfm_set_verbose( level=0, rigor=False, verbose=False )
  • bfm_get_verbose() returns current verbosity level.
bfm_get_verbose( rigor=False, verbose=False )

3.2.2 Bus transaction routines for Python3

  • bfm_write() makes HW BFM generates a burst write transaction.
    • addr for the starting address that should be aligned with the sz.
    • data for the buffer containing byte-stream data to be written, where the size of data buffer should be sz x length.
    • sz for the number of bytes to be written at a each transaction and can be 1, 2, and 4.
    • returns 0 on success, otherwise negative number.
bfm_write( addr, data, sz=4, length=1, rigor=False, verbose=False )
  • bfm_read() makes HW BFM generates a burst read transaction.
    • addr for the starting address that should be aligned with the sz.
    • data for the buffer to be contain byte-stream data after read, where the size of data buffer should be sz x length.
    • sz for the number of bytes to be written at a each transaction and can be 1, 2, and 4.
    • returns 0 on success, otherwise negative number.
bfm_read( addr, data, sz=4, length=1, rigor=False, verbose=False )

4. Hardware side

AMBA AXI BFM is prepare, but other BFM can be easily prepared.

Xilinx Vivado Simulator and Icarus Verilog are used to test this package, but other HDL simulator supporting DPI or VPI can be used without much difficulty.

BFM sends/receives pre-defined packet to/form the host program over IPC channel and following picture shows an example of AMBA AXI BFM, where IRQ is for interrupt, GPIN[31:0]/GPOUT[31:0] is for general purpose ports.

AMBA AXI BFM for Co-simulation
AMBA AXI BFM for Co-simulation

4.1 DPI functions for hardware side

More details can be found from the Verilog code cosim_bfm_dpi.c and cosim_bfm_axi_core.v in this package. Following DPI functions correspond to the that of C API.

  • cosim_ipc_open() creates and opens IPC channel.
import "DPI-C" cosim_ipc_open   =function int cosim_ipc_open   (input int cid);
  • cosim_ipc_close() closes IPC channel.
import "DPI-C" cosim_ipc_close  =function int cosim_ipc_close  (input int cid);
  • cosim_ipc_barrier() waits for joining the software program.
import "DPI-C" cosim_ipc_barrier=function int cosim_ipc_barrier(input int cid);
  • cosim_ipc_get() receives a packet through IPC channel and carries out operation specified by pkt_cmd, which includes read and write transaction.
import "DPI-C" cosim_ipc_get    =function int cosim_ipc_get(
                   input  int       cid  // IPC channel identification
                 , output int       pkt_cmd  // see cosim_bfm_defines.vh
                 , output int       pkt_size  // 1, 2, 4
                 , output int       pkt_length  // burst length
                 , output int       pkt_ack 
                 , output int       pkt_attr
                 , output int       pkt_trans_id
                 , output int       pkt_addr 
                 , output bit [7:0] pkt_data[]  // open-array
                 );
  • cosim_ipc_put() sends a packet through IPC channel and it is usually called after cosim_ipc_get()
import "DPI-C" cosim_ipc_put  =function int cosim_ipc_put(
                   input  int       cid
                 , input  int       pkt_cmd
                 , input  int       pkt_size // 1, 2, 4
                 , input  int       pkt_length // burst length
                 , input  int       pkt_ack
                 , input  int       pkt_attr
                 , input  int       pkt_trans_id
                 , input  int       pkt_addr
                 , input  bit [7:0] pkt_data[] // open-array
                );

4.2 VPI functions for hardware side

More details can be found from the Verilog code cosim_bfm_axi_vpi and cosim_bfm_axi_core.v in this package. Following VPI functions correspond to the that of C API.

  • $cosim_ipc_open() creates and opens IPC channel.
$cosim_ipc_open(cid);
  • cosim_ipc_close() closes IPC channel.
$cosim_ipc_close(cid);
  • cosim_ipc_barrier() waits for joining the software program.
$cosim_ipc_barrier(cid);
  • $cosim_ipc_get() receives a packet through IPC channel and carries out operation specified by pkt_cmd, which includes read and write transaction.
$cosim_ipc_get( cid  // IPC channel identification
              , pkt_cmd  // see cosim_bfm_defines.vh
              , pkt_size  // 1, 2, 4
              , pkt_length  // burst length
              , pkt_ack 
              , pkt_attr
              , pkt_trans_id
              , pkt_addr 
              , pkt_data  // open-array
              );
  • $cosim_ipc_put() sends a packet through IPC channel and it is usually called after cosim_ipc_get()
$cosim_ipc_put( cid
              , pkt_cmd
              , pkt_size // 1, 2, 4
              , pkt_length // burst length
              , pkt_ack
              , pkt_attr
              , pkt_trans_id
              , pkt_addr
              , pkt_data[] // open-array
              );

7. Troubleshooting

To be added.


8. Where to get more information

Source code is available from Ando's GitHub:

Lecture materials and codes of DPI are available from Ando's GitHub:

Lecture materials and codes on AMBA bus are available from Ando's GitHub:

The author has been giving open lecture on AMBA bus at following two institutes:


9. Other things

Author(s)

Acknowledgments

Thanks to all who gave me valuable feedback.

Revision history

  • 2021.08.01: Started by Ando Ki (andoki(at)gmail.com).