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 |
Click to expand table of contents
- Getting started
1.1 Getting started using DPI
1.2 Getting started using VPI - Installation
2.1 DPI for Xilinx xsim
2.2 VPI for Icarus Verilog - Software side
3.1 C API for software side
3.2 Python functions for software side - Hardware side
4.1 DPI functions for hardware side
4.2 VPI functions for hardware side - Troubleshooting
- Where to get more information
- Other things
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.
This program requires followings.
- Shell: Bash
- GNU GCC: C compiler
- HDL simulator: Xilinx Vivado simulator or icarus Verilog
- Python3 if Python interface is required
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 |
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
}
Xilinx Vivado simulator should be installed and available.
Click to expand
- go to 'lib_bfm' directory
$ cd lib_bfm - 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.
- go to example directory
$ cd verification/test_axi_dpi_vpi - 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
- go to example directory
$ cd verification/test_axi_dpi_vpi - 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
Icarus Verilog simulator should be installed and available.
Click to expand
- go to 'lib_bfm' directory
$ cd lib_bfm - 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.
- go to example directory
$ cd verification/test_axi_dpi_vpi - 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
- go to example directory
$ cd verification/test_axi_dpi_vpi - 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
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
Xilinx Vivado simulator should be installed and available.
- go to 'lib_bfm' directory
$ cd lib_bfm - 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.
Icarus Verilog simulator should be installed and available.
- go to 'lib_bfm' directory
$ cd lib_bfm - 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.
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;
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;
}
bfm_open()
tries to create and open communication channel to the hardware simulator, wherecid
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 channelcid
. 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();
bfm_write()
makes HW BFM generates a burst write transaction.addr
for the starting address that should be aligned with thesz
.data
for the buffer containing byte-stream data to be written, where the size ofdata
buffer should besz 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 thesz
.data
for the buffer to be contain byte-stream data after read, where the size ofdata
buffer should besz 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);
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)
LoadCosimLib()
loads C shared library from COSIM_HOME environment variable, wheresimulator
specifies which HDL simulator andxsim
oriverilog
is supported for this version.
LoadCosimLib( simulator, rigor=False, verbose=False)
bfm_open()
tries to create and open communication channel to the hardware simulator, wherecid
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 channelcid
. 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 )
bfm_write()
makes HW BFM generates a burst write transaction.addr
for the starting address that should be aligned with thesz
.data
for the buffer containing byte-stream data to be written, where the size ofdata
buffer should besz 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 thesz
.data
for the buffer to be contain byte-stream data after read, where the size ofdata
buffer should besz 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 )
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 |
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 bypkt_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 aftercosim_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
);
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 bypkt_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 aftercosim_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
);
To be added.
Source code is available from Ando's GitHub:
Lecture materials and codes of DPI are available from Ando's GitHub:
- Tutorial on DPI (Direct Programming Interface): https://github.com/adki/DPI_Tutorial
Lecture materials and codes on AMBA bus are available from Ando's GitHub:
- AMBA lecture materials at GitHub: https://github.com/adki/AMBA_AXI_AHB_APB
The author has been giving open lecture on AMBA bus at following two institutes:
- IDEC (IC Design Education Center) at KAIST: https://www.idec.or.kr
- SW-SoC Academy at ETRI: https://www.asic.net
- Ando Ki - Initial work - Future Design Systems
Thanks to all who gave me valuable feedback.
- 2021.08.01: Started by Ando Ki (andoki(at)gmail.com).