- Some parts of the interface are functions, some are tasks now (whatever fits better). - New methods to read/write the memory and read the program counter as well as to start the simulavrxx tracing functionality. - Fixed a lot of memory leaks.
Signed-off-by: Onno Kortmann <[email protected]> --- configure.ac | 6 + src/Makefile.am | 21 +++- src/avrdevice.h | 2 +- src/verilog/avr.v | 114 +++++++++++++++ src/vpi.cpp | 393 +++++++++++++++++++++++++++++------------------------ 5 files changed, 358 insertions(+), 178 deletions(-) create mode 100644 src/verilog/avr.v diff --git a/configure.ac b/configure.ac index 9b628f8..f48fdfc 100644 --- a/configure.ac +++ b/configure.ac @@ -10,6 +10,12 @@ SWIG_PYTHON AM_CONDITIONAL([USE_SWIG],[test "x$SWIG" != 'x']) AM_CONDITIONAL([USE_PYTHON],[test "x$PYTHON" != 'x']) +AC_CHECK_HEADER([vpi_user.h], + [AC_DEFINE(HAVE_VERILOG, [1], Icarus verilog interface) + WE_HAVE_VERILOG="yes"], + []) +AM_CONDITIONAL([USE_VERILOG], [test "$WE_HAVE_VERILOG"]) + dnl AC_CHECK_PROG(CCACHE, ccache, ccache) diff --git a/src/Makefile.am b/src/Makefile.am index 63a7c27..03a607e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ endif bin_PROGRAMS = simulavr kbdgentables -lib_LTLIBRARIES = libavrsim_pp.la +lib_LTLIBRARIES = libavrsim_pp.la libavrvpi_pp.la libavrsim_pp_la_SOURCES = \ application.cpp \ @@ -64,6 +64,23 @@ libavrsim_pp_la_SOURCES = \ ui.cpp libavrsim_pp_la_LDFLAGS = -version-info 0:0:0 +if USE_VERILOG +libavrvpi_pp_la_SOURCES = \ + $(libavrsim_pp_la_SOURCES) \ + vpi.cpp + +libavrvpi_pp_la_LDFLAGS= \ + -version-info 0:0:0 + +libavrvpi_pp_la_LIBADD= \ + -L $(AVR_LIBIBERTY_INC)/../lib -lbfd $(AVR_LIBIBERTY_LIB) -lz + +all-local: avr.vpi + +avr.vpi: libavrvpi_pp.la + cp .libs/libavrvpi_pp.so avr.vpi +endif + pkginclude_HEADERS = \ application.h \ at4433.h \ @@ -146,7 +163,7 @@ EXTRA_DIST = pysimulavr.i DISTCLEANFILES:=${DISTCLEANFILES} simulavr.so keytrans.h _pysimulavr.so pysimulavr.py pysimulavr_wrap.cpp -Cleanfiles:=${CLEANFILES} simulavr.so keytrans.h simulavr_wrap.cpp +Cleanfiles:=${CLEANFILES} simulavr.so keytrans.h simulavr_wrap.cpp avr.vpi diff --git a/src/avrdevice.h b/src/avrdevice.h index d4ff640..248570f 100644 --- a/src/avrdevice.h +++ b/src/avrdevice.h @@ -95,7 +95,7 @@ class AvrDevice: public SimulationMember { AvrDevice(unsigned int ioSpaceSize, unsigned int IRamSize, unsigned int ERamSize, unsigned int flashSize); /*! Steps the AVR core. - \param untilCoreStepFinished if true, steps a core step and not a + \param untilCoreStepFinished iff true, steps a core step and not a single clock cycle. */ int Step(bool &untilCoreStepFinished, SystemClockOffset *nextStepIn_ns =0); void Reset(); diff --git a/src/verilog/avr.v b/src/verilog/avr.v new file mode 100644 index 0000000..d41a96c --- /dev/null +++ b/src/verilog/avr.v @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Onno Kortmann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* SimulavrXX glue code on the verilog side. */ + +/* FIXME: Some parts are still unfinished! + FIXME: Output-pullups are not implemented yet - find a good way to do that! + */ + +module avr_pin(conn); + parameter name="UNSPECIFIED"; + inout conn; + wire out; + + integer val; + + wire output_active; + assign output_active = (val<=2); + + assign conn = output_active ? out : 1'bz; + + function a2v; + input apin; + if (apin==0) // low + a2v=0; + else if (apin==1) // high + a2v=1; + else if (apin==2) // shorted + a2v=1'bx; + else if (apin==3) // pull-up + a2v=1; + else if (apin==4) // tristate + a2v=1'bz; + else if (apin==5) // pull-down + a2v=0; + else if (apin==6) // analog + a2v=1'bx; + else if (apin==7) // analog, shorted + a2v=1'bx; + endfunction // a2v + + function v2a; + input vpin; + if (vpin==1'bz) + v2a=4; // tristate + else if (vpin==1'bx) + v2a=2; // approximate as shorted + else if (vpin==1) + v2a=1; // high + else if (vpin==0) + v2a=0; // low + endfunction // v2a + + assign out=a2v(val); + + always @(posedge core.clk) begin + val=$avr_get_pin(core.handle, name); + $avr_set_pin(core.handle, name, v2a(conn)); + end + +endmodule // avr_pin + +module avr_clock(clk); + output clk; + reg clk; + parameter FREQ=4_000_000; + initial begin + clk<=0; + end + + always @(clk) begin + #(1_000_000_000/FREQ/2) clk<=~clk; //125000 -> 4MHz clock + end +endmodule // avr_clock + +module AVRCORE(clk); + parameter progfile="UNSPECIFIED"; + parameter name="UNSPECIFIED"; + input clk; + + integer handle; + integer PCw; // word-wise PC as it comes from simulavrxx + wire [16:0] PCb; // byte-wise PC as used in output from avr-objdump! + assign PCb=2*PCw; + + initial begin + $display("Creating an AVR device."); + handle=$avr_create(name, progfile); + //$avr_reset(handle); + end + + always @(posedge clk) begin + PCw=$avr_get_pc(handle); + $avr_tick(handle); + end + +endmodule // AVRCORE + diff --git a/src/vpi.cpp b/src/vpi.cpp index a644f13..5884276 100644 --- a/src/vpi.cpp +++ b/src/vpi.cpp @@ -23,6 +23,9 @@ #include <vpi_user.h> #include "avrdevice.h" #include "avrfactory.h" +#include "rwmem.h" +#include "trace.h" + static std::vector<AvrDevice*> devices; @@ -41,57 +44,100 @@ static bool checkHandle(int h) { return true; } +#define VPI_UNPACKS(name) \ + { \ + vpiHandle name = vpi_scan(argv); \ + if (! name) { \ + vpi_printf("%s: " #name " parameter missing.\n", xx);\ + vpi_free_object(argv); \ + return 0; \ + } \ + value.format = vpiStringVal; \ + vpi_get_value(name, &value); \ + } \ + std::string name = value.value.str; + +#define VPI_UNPACKI(name) \ + { \ + vpiHandle name = vpi_scan(argv); \ + if (! name) { \ + vpi_printf("%s: " #name " parameter missing.\n", xx);\ + vpi_free_object(argv); \ + return 0; \ + } \ + value.format = vpiIntVal; \ + vpi_get_value(name, &value); \ + } \ + int name = value.value.integer; + +#define VPI_RETURN_INT(val) \ + value.format = vpiIntVal; \ + value.value.integer = (val); \ + vpi_put_value(ch, &value, 0, vpiNoDelay); \ + return 0; + +#define AVR_HCHECK() \ + if (!checkHandle(handle)) { \ + vpi_printf("%s: Invalid handle parameter.\n", xx); \ + return 0; \ + } + +#define VPI_BEGIN() \ + s_vpi_value value; \ + vpiHandle ch = vpi_handle(vpiSysTfCall, 0); \ + vpiHandle argv = vpi_iterate(vpiArgument, ch); + +#define VPI_END() \ + vpi_free_object(argv); + +#define VPI_REGISTER_TASK(name) \ + { \ + s_vpi_systf_data tf_data; \ + tf_data.type = vpiSysTask; \ + tf_data.tfname = "$" #name; \ + tf_data.calltf = name ## _tf; \ + tf_data.compiletf = 0; \ + tf_data.sizetf = 0; \ + tf_data.user_data = "$" #name; \ + vpi_register_systf(&tf_data); \ + } + +#define VPI_REGISTER_FUNC(name) \ + { \ + s_vpi_systf_data tf_data; \ + tf_data.type = vpiSysFunc; \ + tf_data.tfname = "$" #name; \ + tf_data.calltf = name ## _tf; \ + tf_data.compiletf = 0; \ + tf_data.sizetf = 0; \ + tf_data.user_data = "$" #name; \ + vpi_register_systf(&tf_data); \ + } + /*! This function creates a new AVR core and `returns' a handle to it Usage from Verilog: - $avr_create(handle, device, progname) + $avr_create(device, progname) -> handle where handle is an integer handle by which the avr can be accessed in all other calls here - devic is the name of the AVR device to create + device is the name of the AVR device to create progname is the path to the flash program elf binary */ static PLI_INT32 avr_create_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - vpiHandle _device=vpi_scan(argv); - vpiHandle _progname=vpi_scan(argv); - - value.format = vpiStringVal; - vpi_get_value(_device, &value); - std::string device=value.value.str; - - value.format = vpiStringVal; - vpi_get_value(_progname, &value); - std::string progname=value.value.str; - - // FIXME: Better error handling than exit(...) here. Exceptions! - AvrDevice* dev; - devices.push_back(dev=AvrFactory::instance().makeDevice(device)); + VPI_BEGIN(); + VPI_UNPACKS(device); + VPI_UNPACKS(progname); + VPI_END(); - /* - if (device=="AT90S4433") { - devices.push_back(dev=new AvrDevice_at90s4433()); - } else { - vpi_printf("Invalid AVR device: %s\n", device.c_str()); - vpi_control(vpiFinish, 1); - return 0; - } - if (!dev) { - vpi_printf("Can't create backend AVR simulavrxx device."); - vpi_control(vpiFinish, 1); - return 0; - }*/ + AvrDevice* dev=AvrFactory::instance().makeDevice(device); + devices.push_back(dev); dev->Load(progname.c_str()); - - value.format = vpiIntVal; - value.value.integer = devices.size()-1; - vpi_put_value(handle, &value, 0, vpiNoDelay); + + VPI_RETURN_INT(devices.size()-1); } /*! @@ -101,17 +147,12 @@ static PLI_INT32 avr_create_tf(char *xx) { $avr_reset(handle) */ static PLI_INT32 avr_reset_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - - value.format = vpiIntVal; - vpi_get_value(handle, &value); - int h = value.value.integer; - if (!checkHandle(h)) return 0; + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_END(); - devices[h]->Reset(); + AVR_HCHECK(); + devices[handle]->Reset(); return 0; } @@ -122,20 +163,16 @@ static PLI_INT32 avr_reset_tf(char *xx) { $avr_destroy(handle) */ static PLI_INT32 avr_destroy_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - - value.format = vpiIntVal; - vpi_get_value(handle, &value); - int h = value.value.integer; - if (!checkHandle(h)) return 0; + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_END(); - /* We leak a bit of memory for the pointer in the vector, + AVR_HCHECK(); + + /* We may leak a bity of memory for the pointer in the vector, but... what the hell! */ - delete devices[h]; - devices[h]=0; + delete devices[handle]; + devices[handle]=0; return 0; } @@ -146,53 +183,34 @@ static PLI_INT32 avr_destroy_tf(char *xx) { $avr_tick(handle) */ static PLI_INT32 avr_tick_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - - value.format = vpiIntVal; - vpi_get_value(handle, &value); - int h = value.value.integer; - if (!checkHandle(h)) return 0; + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_END(); + + AVR_HCHECK(); - /* Lets do a HARDWARE step in the AVR core. - uC stepping in opcode units does not seem to make any sense from this - HDL view. But it looks like avrdevice does have no interest in this - value at all? */ bool no_hw=false; - devices[h]->Step(no_hw); + devices[handle]->Step(no_hw); return 0; } /*! This function reads an AVR pin value. Usage from verilog: - $avr_pin_get(handle, name, value) + $avr_pin_get(handle, name) -> value */ static PLI_INT32 avr_get_pin_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - vpiHandle _name=vpi_scan(argv); - vpiHandle _value=vpi_scan(argv); - value.format = vpiIntVal; - vpi_get_value(handle, &value); - int h = value.value.integer; - if (!checkHandle(h)) return 0; - - value.format = vpiStringVal; - vpi_get_value(_name, &value); - std::string name=value.value.str; - - Pin *pin=devices[h]->GetPin(name.c_str()); + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_UNPACKS(name); + VPI_END(); + + AVR_HCHECK(); + + Pin *pin=devices[handle]->GetPin(name.c_str()); int ret(pin->outState); - value.format = vpiIntVal; - value.value.integer = ret; - vpi_put_value(_value, &value, 0, vpiNoDelay); - return 0; + VPI_RETURN_INT(ret); } /*! @@ -201,98 +219,123 @@ static PLI_INT32 avr_get_pin_tf(char *xx) { $avr_pin_set(handle, name, val) */ static PLI_INT32 avr_set_pin_tf(char *xx) { - s_vpi_value value; - vpiHandle ch = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, ch); - vpiHandle handle=vpi_scan(argv); - vpiHandle _name=vpi_scan(argv); - vpiHandle _value=vpi_scan(argv); - value.format = vpiIntVal; - vpi_get_value(handle, &value); - int h = value.value.integer; - if (!checkHandle(h)) return 0; - - value.format = vpiStringVal; - vpi_get_value(_name, &value); - std::string name=value.value.str; - - Pin *pin=devices[h]->GetPin(name.c_str()); - - value.format = vpiIntVal; - vpi_get_value(_value, &value); - int val=value.value.integer; + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_UNPACKS(name); + VPI_UNPACKI(val); + VPI_END(); + + AVR_HCHECK(); + + Pin *pin=devices[handle]->GetPin(name.c_str()); + /* FIXME: Simply exports AVR pin states to verilog. This - a breach of abstractions. */ + may be considered a breach of abstractions. + */ pin->SetInState(Pin::T_Pinstate(val)); return 0; } -static void register_tasks() { - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_create"; - tf_data.calltf = avr_create_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); - } - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_reset"; - tf_data.calltf = avr_reset_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); - } - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_destroy"; - tf_data.calltf = avr_destroy_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); - } +/*! + This function reads the value of the program counter + in the AVR. + Usage from verilog: + $avr_get_pc(handle) -> pc_value +*/ +static PLI_INT32 avr_get_pc_tf(char *xx) { + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_END(); - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_tick"; - tf_data.calltf = avr_tick_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); - } + AVR_HCHECK(); - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_get_pin"; - tf_data.calltf = avr_get_pin_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); - } + VPI_RETURN_INT(devices[handle]->PC); +} + +/*! + This function reads the value of a RAM-readable + location in the AVR (0..31 are the regs, followed by the IO-space etc). + Usage from verilog: + $avr_get_rw(handle, adr) -> val + where + adr is the adress to read + and val is the returned value at that address +*/ +static PLI_INT32 avr_get_rw_tf(char *xx) { + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_UNPACKI(address); + VPI_END(); + + AVR_HCHECK(); + + VPI_RETURN_INT(*(devices[handle]->rw[address])); +} + +/*! + Counterpart to avr_get_rw_tf. It sets the value of a RAM-readable + location in the AVR (0..31 are the regs, followed by the IO-space etc). + Usage from verilog: + $avr_set_rw(handle, adr, val) + where + adr is the adress to read + and val is the value to set at that address +*/ +static PLI_INT32 avr_set_rw_tf(char *xx) { + VPI_BEGIN(); + VPI_UNPACKI(handle); + VPI_UNPACKI(address); + VPI_UNPACKI(val); + VPI_END(); + + AVR_HCHECK(); + + *(devices[handle]->rw[address])=val; + return 0; +} + +/*! + Enable or disable tracing for all AVR core. + TODO: Implement tracing per core? + + Usage from Verilog: + + $avr_trace(tracename) - { - s_vpi_systf_data tf_data; - - tf_data.type = vpiSysTask; - tf_data.tfname = "$avr_set_pin"; - tf_data.calltf = avr_set_pin_tf; - tf_data.compiletf = 0; - tf_data.sizetf = 0; - vpi_register_systf(&tf_data); + where + tracename is the output file name for tracing. If it is the empty string, + tracing will be disabled again and the file will be closed. +*/ + +static PLI_INT32 avr_trace_tf(char *xx) { + VPI_BEGIN(); + VPI_UNPACKS(tracename); + VPI_END(); + + if (tracename.length()) { + traceOut.open(tracename.c_str()); + for (size_t i=0; i < devices.size(); i++) + devices[i]->trace_on=1; + } else { + traceOut.close(); + for (size_t i=0; i < devices.size(); i++) + devices[i]->trace_on=0; } } +static void register_tasks() { + VPI_REGISTER_FUNC(avr_create); + VPI_REGISTER_TASK(avr_reset); + VPI_REGISTER_TASK(avr_destroy); + VPI_REGISTER_TASK(avr_tick); + VPI_REGISTER_FUNC(avr_get_pin); + VPI_REGISTER_TASK(avr_set_pin); + VPI_REGISTER_FUNC(avr_get_pc); + VPI_REGISTER_FUNC(avr_get_rw); + VPI_REGISTER_TASK(avr_set_rw); + VPI_REGISTER_TASK(avr_trace); +} + /* This is a table of register functions. This table is the external symbol that the simulator looks for when loading this .vpi module. */ void (*vlog_startup_routines[])() = { -- 1.5.6.5 _______________________________________________ Simulavr-devel mailing list [email protected] http://lists.nongnu.org/mailman/listinfo/simulavr-devel
