Qemu is an open-source processor emulator and can be freely downloaded from www.qemu.org. It is a fast open-source processor emulator and its performance is very close to Host machine performance as it does dynamic translation of Guest instructions into Host machine instructions and run them on Host thus decreasing the boot-time & run-time of the applications running on the simulated platform. Qemu is completely implemented in ‘C’ language and have its own simulation engine. It supports various CPU architectures; ARM, Micro blaze, PowerPC, Sparc to a name a few and also allows us to enhance or add new CPU architecture. It also supports various IP models and reference boards using which one can readily bring-up a SoC and run either bare-metal application or full-fledged OS.
As we are aware, SystemC is a standard language for the modelling of SoC & Electronics system. It is becoming a language of choice for SoC companies and Electronics system companies to develop virtual prototypes of their SoC & Electronic system. SystemC being a standard, allows the interoperability of models from various third parties, and can simulate in the popular virtual prototyping tools.
Being discussed about the two simulators, one would obviously try to leverage the mature Qemu CPU models and reference boards in their SystemC Virtual Prototypes. But there are certain road blocks to do this. Qemu is entirely implemented in ‘C’ language and uses its own simulation engine. It does not inter operate with SystemC simulation engine, ports, interfaces, signals, events etc., Hence QEMU does not fit with the SystemC based design and verification flows. Lots of models and reference boards can be readily used without having to develop everything from scratch if we enable Qemu’s integration with SystemC.
In this article we discuss about how we can enable this integration.
Extend Qemu with SystemC – CST_QEMU
The solution is to extend Qemu with SystemC. We have developed CST_QEMU by developing a SystemC / TLM2.0 wrapper over QEMU. CST_QEMU implements the following features.
systemc_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sc_timer_cb, _model_wrapper);
timer_mod(systemc_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
static void sc_timer_cb(void *opaque)
{
int64_t qemu_time_vm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t next_systemc_qemu_time;
next_systemc_qemu_time = time_based_qemu_cpu_sync_sc(opaque, qemu_time_vm);
timer_mod(systemc_timer, next_systemc_qemu_time);
classPtr->m_quantum_keeper.sync();
}
/**< Master socket. */
tlm_utils::simple_initiator_socket<cst_cpu_base, 64> t_master_socket;
/**< Access Functions. */
// performs a TLM write transaction
virtual int CPU_write_access(uint64_t, uint8_t *, uint32_t);
// performs a TLM read transaction
virtual int CPU_read_access(uint64_t, uint8_t *, uint32_t);
// perform a direct memory access.
virtual uint8_t * CPU_direct_memory_access(uint64_t, uint32_t , uint32_t *);
void IRQ_update();
void RAISE_IRQ(int irq_num);
void LOWER_IRQ(int irq_num);
void register_memory_map();
ops->read = dmi_cpu_read;
ops->write = dmi_cpu_write;
else
{
ops->read = non_dmi_cpu_read;
ops->write = non_dmi_cpu_write;
}
ops->endianness = info.endianness;
ops->impl.min_access_size = 0;
ops->impl.max_access_size = 0;
ops->valid.min_access_size = 0;
ops->valid.max_access_size = 0;
ops->valid.accepts = NULL;
//NOTE: Cry.Memory leaking here.FIX IT.
OPAQUE_st *param = (OPAQUE_st*)malloc(sizeof(OPAQUE_st));
param->prnt = model_wrapper;
param->start_address = info.start_address;
memory_region_init_io(ram, NULL, ops, (void*)param, info.rgn_name, info.size);
memory_region_add_subregion(sysmem, info.start_address, ram)
//HACK to write into TLM memory
uint8_t *ram_ptr = NULL;
uint32_t ram_size = 0;
if(qemu_sc_direct_memory_rw != NULL)
ram_ptr = qemu_sc_direct_memory_rw(model_wrapper,addr,rom->datasize,&ram_size);
else
printf("ERROR:qemu_sc_direct_memory_rw is NULL\n");
//printf("DEBUG: ram_ptr=0x%x,addr=0x%x,romsize=0x%x, datasize=0x%x, ram_sz=0x%x\n",
//ram_ptr,addr, rom->romsize, rom->datasize, ram_size);
if(ram_ptr != NULL) {
memcpy(ram_ptr, rom->data, rom->datasize);
} else {
As we are aware, SystemC is a standard language for the modelling of SoC & Electronics system. It is becoming a language of choice for SoC companies and Electronics system companies to develop virtual prototypes of their SoC & Electronic system. SystemC being a standard, allows the interoperability of models from various third parties, and can simulate in the popular virtual prototyping tools.
Being discussed about the two simulators, one would obviously try to leverage the mature Qemu CPU models and reference boards in their SystemC Virtual Prototypes. But there are certain road blocks to do this. Qemu is entirely implemented in ‘C’ language and uses its own simulation engine. It does not inter operate with SystemC simulation engine, ports, interfaces, signals, events etc., Hence QEMU does not fit with the SystemC based design and verification flows. Lots of models and reference boards can be readily used without having to develop everything from scratch if we enable Qemu’s integration with SystemC.
In this article we discuss about how we can enable this integration.
Extend Qemu with SystemC – CST_QEMU
The solution is to extend Qemu with SystemC. We have developed CST_QEMU by developing a SystemC / TLM2.0 wrapper over QEMU. CST_QEMU implements the following features.
- Synchronising Qemu and SystemC simulation engines.We have implemented two different mechanisms for synchronization, one is to synchronize after every N number of instructions, and the other is to synchronize after certain time quantum. In both cases, user can configure the frequency of synchronization, depending on the speed and timing accuracy requirement.
systemc_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sc_timer_cb, _model_wrapper);
timer_mod(systemc_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
static void sc_timer_cb(void *opaque)
{
int64_t qemu_time_vm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t next_systemc_qemu_time;
next_systemc_qemu_time = time_based_qemu_cpu_sync_sc(opaque, qemu_time_vm);
timer_mod(systemc_timer, next_systemc_qemu_time);
classPtr->m_quantum_keeper.sync();
}
- Bus interface exposed through TLM2.0 initiator socket to connect the external SystemC IP models
/**< Master socket. */
tlm_utils::simple_initiator_socket<cst_cpu_base, 64> t_master_socket;
/**< Access Functions. */
// performs a TLM write transaction
virtual int CPU_write_access(uint64_t, uint8_t *, uint32_t);
// performs a TLM read transaction
virtual int CPU_read_access(uint64_t, uint8_t *, uint32_t);
// perform a direct memory access.
virtual uint8_t * CPU_direct_memory_access(uint64_t, uint32_t , uint32_t *);
- Interconnection between SystemC signals and Qemu signals sc_in<bool> IRQ[64];
void IRQ_update();
void RAISE_IRQ(int irq_num);
void LOWER_IRQ(int irq_num);
- Configurable address spaceUser can specify what all address space will be accessed from within QEMU, and what will go out to the SystemC world. This functionality allows users to use the existing reference board from QEMU, and connect the external SystemC models at vacant address space.
void register_memory_map();
ops->read = dmi_cpu_read;
ops->write = dmi_cpu_write;
else
{
ops->read = non_dmi_cpu_read;
ops->write = non_dmi_cpu_write;
}
ops->endianness = info.endianness;
ops->impl.min_access_size = 0;
ops->impl.max_access_size = 0;
ops->valid.min_access_size = 0;
ops->valid.max_access_size = 0;
ops->valid.accepts = NULL;
//NOTE: Cry.Memory leaking here.FIX IT.
OPAQUE_st *param = (OPAQUE_st*)malloc(sizeof(OPAQUE_st));
param->prnt = model_wrapper;
param->start_address = info.start_address;
memory_region_init_io(ram, NULL, ops, (void*)param, info.rgn_name, info.size);
memory_region_add_subregion(sysmem, info.start_address, ram)
- Flashing the binary, when SystemC IP is used as ROM/flash.
//HACK to write into TLM memory
uint8_t *ram_ptr = NULL;
uint32_t ram_size = 0;
if(qemu_sc_direct_memory_rw != NULL)
ram_ptr = qemu_sc_direct_memory_rw(model_wrapper,addr,rom->datasize,&ram_size);
else
printf("ERROR:qemu_sc_direct_memory_rw is NULL\n");
//printf("DEBUG: ram_ptr=0x%x,addr=0x%x,romsize=0x%x, datasize=0x%x, ram_sz=0x%x\n",
//ram_ptr,addr, rom->romsize, rom->datasize, ram_size);
if(ram_ptr != NULL) {
memcpy(ram_ptr, rom->data, rom->datasize);
} else {
CST_BOARD_ZYNQ
In QEMU there is a reference board for Xilinx Zynq 7000. This virtual platform is implemented by Xilinx and is capable to boot Linux. It represents the virtual model of fixed SoC portion of Zynq 7000. This reference board is extended by CircuitSutra using CST_QEMU, this allows to integrate the SystemC models of additional IP.
This gives us extendable virtual platform of Xilinx Zynq 7000. This virtual platform has two portions. The fixed SoC portion is represented by the model inside QEMU. The design IP that needs to go into the FPGA portion can be modelled using SystemC / TLM2.0 and connected at the vacant address space. Such a setup provides a very powerful development environment to the users of Xilinx Zynq 7000. They can use this virtual platform for the device driver / firmware development for the additional design IP that goes in FPGA portion, or for the embedded application development for the entire system. It also enables the powerful automated unit testing of firmware / embedded SW.
Results
Using the above methodology, reference virtual platforms were developed mixing the CPU models and IP models from QEMU with the IP models in SystemC.
Summary
CST_QEMU enables one to use rich QEMU infrastructure in SystemC based virtual prototypes The above discussed methodology can be used for embedded SW development, IP development & testing, automated unit testing of firmware, etc., This methodology is successfully deployed by several customers in their production environment.
References
In QEMU there is a reference board for Xilinx Zynq 7000. This virtual platform is implemented by Xilinx and is capable to boot Linux. It represents the virtual model of fixed SoC portion of Zynq 7000. This reference board is extended by CircuitSutra using CST_QEMU, this allows to integrate the SystemC models of additional IP.
This gives us extendable virtual platform of Xilinx Zynq 7000. This virtual platform has two portions. The fixed SoC portion is represented by the model inside QEMU. The design IP that needs to go into the FPGA portion can be modelled using SystemC / TLM2.0 and connected at the vacant address space. Such a setup provides a very powerful development environment to the users of Xilinx Zynq 7000. They can use this virtual platform for the device driver / firmware development for the additional design IP that goes in FPGA portion, or for the embedded application development for the entire system. It also enables the powerful automated unit testing of firmware / embedded SW.
Results
Using the above methodology, reference virtual platforms were developed mixing the CPU models and IP models from QEMU with the IP models in SystemC.
Summary
CST_QEMU enables one to use rich QEMU infrastructure in SystemC based virtual prototypes The above discussed methodology can be used for embedded SW development, IP development & testing, automated unit testing of firmware, etc., This methodology is successfully deployed by several customers in their production environment.
References