diff --git a/.gitignore b/.gitignore index 62e3790b..7fbe72d9 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ modelsim.ini transcript *.log work/ +work_nvc/ work_vlog/ work_vcom/ *.wlf diff --git a/.travis.yml b/.travis.yml index f525fcce..35fbacdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ python: - "2.7" - "pypy" - "3.4" + - "3.5" addons: apt: @@ -21,15 +22,19 @@ install: env: - CI_TARGET=core - - CI_TARGET=icarus + - CI_TARGET=iverilog - CI_TARGET=ghdl -matrix: - allow_failures: - - python: "3.4" - env: CI_TARGET=icarus - - python: "3.4" - env: CI_TARGET=ghdl +# matrix: +# allow_failures: +# - python: "3.4" +# env: CI_TARGET=iverilog +# - python: "3.4" +# env: CI_TARGET=ghdl +# - python: "3.5" +# env: CI_TARGET=iverilog +# - python: "3.5" +# env: CI_TARGET=ghdl script: ./scripts/ci.sh diff --git a/CHANGES.txt b/CHANGES.txt index 9fca3052..67d6108b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,10 @@ +Release 0.9.0 11-Jul-2015 +------------------------- + +Full details about new features and changes can be found here: + + http://docs.myhdl.org/en/latest/whatsnew/0.9.html + Release 0.8.1 26-Aug-2014 ------------------------- diff --git a/README.md b/README.md index e277689c..addf9baa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -MyHDL 0.9 -========= +MyHDL 1.0dev +============ -[![Documentation Status](https://readthedocs.org/projects/myhdl/badge/?version=master)](http://docs.myhdl.org/en/latest/manual) +[![Join the chat at https://gitter.im/jandecaluwe/myhdl](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jandecaluwe/myhdl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[![Documentation Status](https://readthedocs.org/projects/myhdl/badge/?version=stable)](http://docs.myhdl.org/en/stable/manual) +[![Documentation Status](https://readthedocs.org/projects/myhdl/badge/?version=latest)](http://docs.myhdl.org/en/latest/manual) [![Build Status](https://travis-ci.org/jandecaluwe/myhdl.svg?branch=master)](https://travis-ci.org/jandecaluwe/myhdl) What is MyHDL? @@ -25,13 +28,13 @@ Documentation ------------- The manual is available on-line: - - http://docs.myhdl.org/en/latest/manual + - http://docs.myhdl.org/en/stable/manual What's new ---------- To find out what's new in this release, please read: - - http://docs.myhdl.org/en/latest/whatsnew/0.9.html + - http://docs.myhdl.org/en/stable/whatsnew/0.9.html Installation ------------ diff --git a/cosimulation/modelsim-win/Makefile b/cosimulation/modelsim-win/Makefile new file mode 100644 index 00000000..30f56f47 --- /dev/null +++ b/cosimulation/modelsim-win/Makefile @@ -0,0 +1,21 @@ +INCS := C:\\modeltech64_10.4\\include +LIB_PATH := C:\\modeltech64_10.4\\win64 + +ifneq ($(filter cl%,$(CC)),) + CFLAGS := /LD /I$(INCS) + LIBFLAGS := $(LIB_PATH)\\mtipli.lib +else + CFLAGS := -static -g -I$(INCS) -o myhdl_vpi.dll + LIBFLAGS := -L$(LIB_PATH) -lmtipli +endif + +all: myhdl_vpi.dll + +myhdl_vpi.dll: myhdl_vpi.c + $(CC) $(CFLAGS) $< $(LIBFLAGS) + +clean: + @del /q myhdl_vpi.dll || rm -rf myhdl_vpi.dll 2>&1> /dev/null ||: + @del /q myhdl_vpi.lib || rm -rf myhdl_vpi.lib 2>&1> /dev/null ||: + @del /q myhdl_vpi.exp || rm -rf myhdl_vpi.exp 2>&1> /dev/null ||: + @del /q myhdl_vpi.obj || rm -rf myhdl_vpi.obj 2>&1> /dev/null ||: diff --git a/cosimulation/modelsim-win/myhdl_vpi.c b/cosimulation/modelsim-win/myhdl_vpi.c new file mode 100644 index 00000000..fdc03c63 --- /dev/null +++ b/cosimulation/modelsim-win/myhdl_vpi.c @@ -0,0 +1,532 @@ + +#include +#include +#include +#include +#include +#include "vpi_user.h" +#include "sv_vpi_user.h" + +#define MAXLINE 4096 +#define MAXWIDTH 10 +#define MAXARGS 64 +// #define DEBUG 1 + +/* Sized variables */ +#ifndef PLI_TYPES +#define PLI_TYPES +typedef int PLI_INT32; +typedef unsigned int PLI_UINT32; +typedef short PLI_INT16; +typedef unsigned short PLI_UINT16; +typedef char PLI_BYTE8; +typedef unsigned char PLI_UBYTE8; +#endif + +/* 64 bit type for time calculations */ +typedef unsigned long long myhdl_time64_t; + +static HANDLE rpipe; +static HANDLE wpipe; + +static vpiHandle from_myhdl_systf_handle = NULL; +static vpiHandle to_myhdl_systf_handle = NULL; + +static char changeFlag[MAXARGS]; + +static char bufcp[MAXLINE]; + +static myhdl_time64_t myhdl_time; +static myhdl_time64_t verilog_time; +static myhdl_time64_t pli_time; +static int delta; + +/* prototypes */ +static PLI_INT32 from_myhdl_calltf(PLI_BYTE8 *user_data); +static PLI_INT32 to_myhdl_calltf(PLI_BYTE8 *user_data); +static PLI_INT32 readonly_callback(p_cb_data cb_data); +static PLI_INT32 delay_callback(p_cb_data cb_data); +static PLI_INT32 delta_callback(p_cb_data cb_data); +static PLI_INT32 change_callback(p_cb_data cb_data); + +static int init_pipes(); + +static myhdl_time64_t timestruct_to_time(const struct t_vpi_time*ts); + +/* from Icarus */ +static myhdl_time64_t timestruct_to_time(const struct t_vpi_time*ts) +{ + myhdl_time64_t ti = ts->high; + ti <<= 32; + ti += ts->low & 0xffffffff; + return ti; +} + +static int read_pipe(void *buf, size_t count) +{ + DWORD read; + + if (!ReadFile(rpipe, buf, (DWORD)count, &read, NULL)) + { + return -1; + } + + return (int)read; +} + +static int write_pipe(const void *buf, size_t count) +{ + DWORD written; + + if (!WriteFile(wpipe, buf, (DWORD)count, &written, NULL)) + { + return -1; + } + + return (int)written; +} + +static int init_pipes() +{ + char *w; + char *r; + + static int init_pipes_flag = 0; + + if (init_pipes_flag) { + return(0); + } + + if ((w = getenv("MYHDL_TO_PIPE")) == NULL) { + vpi_printf("ERROR: no write pipe to myhdl\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + if ((r = getenv("MYHDL_FROM_PIPE")) == NULL) { + vpi_printf("ERROR: no read pipe from myhdl\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + wpipe = (HANDLE)atoi(w); + rpipe = (HANDLE)atoi(r); + init_pipes_flag = 1; + return (0); +} + +static PLI_INT32 from_myhdl_calltf(PLI_BYTE8 *user_data) +{ + vpiHandle reg_iter, reg_handle; + s_vpi_time verilog_time_s; + char buf[MAXLINE]; + char s[MAXWIDTH]; + int n; + + static int from_myhdl_flag = 0; + + if (from_myhdl_flag) { + vpi_printf("ERROR: $from_myhdl called more than once\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + from_myhdl_flag = 1; + + init_pipes(); + + verilog_time_s.type = vpiSimTime; + vpi_get_time(NULL, &verilog_time_s); + verilog_time = timestruct_to_time(&verilog_time_s); + if (verilog_time != 0) { + vpi_printf("ERROR: $from_myhdl should be called at time 0\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + sprintf(buf, "FROM 0 "); + pli_time = 0; + delta = 0; + + from_myhdl_systf_handle = vpi_handle(vpiSysTfCall, NULL); + reg_iter = vpi_iterate(vpiArgument, from_myhdl_systf_handle); + while ((reg_handle = vpi_scan(reg_iter)) != NULL) { + if (vpi_get(vpiType, reg_handle) != vpiReg) { + vpi_printf("ERROR: $from_myhdl argument %s should be a reg\n", + vpi_get_str(vpiName, reg_handle)); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + strcat(buf, vpi_get_str(vpiName, reg_handle)); + strcat(buf, " "); + sprintf(s, "%d ", vpi_get(vpiSize, reg_handle)); + strcat(buf, s); + vpi_free_object(reg_handle); + } + //vpi_free_object(reg_iter); + + n = write_pipe(buf, strlen(buf)); + + if ((n = read_pipe(buf, MAXLINE)) <= 0) { + vpi_printf("Info: MyHDL simulator down\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + assert(n > 0); + buf[n] = '\0'; + + return(0); +} + +static PLI_INT32 to_myhdl_calltf(PLI_BYTE8 *user_data) +{ + vpiHandle net_iter, net_handle, cb_h; + char buf[MAXLINE]; + char s[MAXWIDTH]; + int n; + int i; + int *id; + s_cb_data cb_data_s; + s_vpi_time verilog_time_s; + s_vpi_time time_s; + s_vpi_value value_s; + static int to_myhdl_flag = 0; + + if (to_myhdl_flag) { + vpi_printf("ERROR: $to_myhdl called more than once\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + to_myhdl_flag = 1; + + init_pipes(); + + verilog_time_s.type = vpiSimTime; + vpi_get_time(NULL, &verilog_time_s); + verilog_time = timestruct_to_time(&verilog_time_s); + if (verilog_time != 0) { + vpi_printf("ERROR: $to_myhdl should be called at time 0\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + sprintf(buf, "TO 0 "); + pli_time = 0; + delta = 0; + + time_s.type = vpiSuppressTime; + value_s.format = vpiSuppressVal; + cb_data_s.reason = cbValueChange; + cb_data_s.cb_rtn = change_callback; + cb_data_s.time = &time_s; + cb_data_s.value = &value_s; + // value_s.format = vpiHexStrVal; + i = 0; + to_myhdl_systf_handle = vpi_handle(vpiSysTfCall, NULL); + net_iter = vpi_iterate(vpiArgument, to_myhdl_systf_handle); + while ((net_handle = vpi_scan(net_iter)) != NULL) { + if (i == MAXARGS) { + vpi_printf("ERROR: $to_myhdl max #args (%d) exceeded\n", MAXARGS); + vpi_control(vpiFinish, 1); /* abort simulation */ + } + strcat(buf, vpi_get_str(vpiName, net_handle)); + strcat(buf, " "); + sprintf(s, "%d ", vpi_get(vpiSize, net_handle)); + strcat(buf, s); + changeFlag[i] = 0; + id = malloc(sizeof(int)); + *id = i; + cb_data_s.user_data = (PLI_BYTE8 *)id; + cb_data_s.obj = net_handle; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + i++; + vpi_free_object(net_handle); + } + //vpi_free_object(net_iter); + + n = write_pipe(buf, strlen(buf)); + + if ((n = read_pipe(buf, MAXLINE)) <= 0) { + vpi_printf("ABORT from $to_myhdl\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + buf[n] = '\0'; + assert(n > 0); + + // register read-only callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 0; + cb_data_s.reason = cbReadOnlySynch; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = readonly_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + // pre-register delta cycle callback // + delta = 0; + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 1; + cb_data_s.reason = cbAfterDelay; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = delta_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + return(0); +} + + +static PLI_INT32 readonly_callback(p_cb_data cb_data) +{ + vpiHandle net_iter, net_handle, cb_h; + s_cb_data cb_data_s; + s_vpi_time verilog_time_s; + s_vpi_value value_s; + s_vpi_time time_s; + char buf[MAXLINE]; + int n; + int i; + char *myhdl_time_string; + myhdl_time64_t delay; + + static int start_flag = 1; + + if (start_flag) { + start_flag = 0; + n = write_pipe("START", 5); + // vpi_printf("INFO: RO cb at start-up\n"); + if ((n = read_pipe(buf, MAXLINE)) <= 0) { + vpi_printf("ABORT from RO cb at start-up\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + } + assert(n > 0); + } + + buf[0] = '\0'; + verilog_time_s.type = vpiSimTime; + vpi_get_time(NULL, &verilog_time_s); + verilog_time = timestruct_to_time(&verilog_time_s); + if (verilog_time != (pli_time * 1000 + delta)) { + vpi_printf("%u %u\n", verilog_time_s.high, verilog_time_s.low ); + vpi_printf("%llu %llu %d\n", verilog_time, pli_time, delta); + } + /* Icarus 0.7 fails on this assertion beyond 32 bits due to a bug */ + // assert(verilog_time == pli_time * 1000 + delta); + assert( (verilog_time & 0xFFFFFFFF) == ( (pli_time * 1000 + delta) & 0xFFFFFFFF ) ); + sprintf(buf, "%llu ", pli_time); + net_iter = vpi_iterate(vpiArgument, to_myhdl_systf_handle); + value_s.format = vpiHexStrVal; + i = 0; + while ((net_handle = vpi_scan(net_iter)) != NULL) { + if (changeFlag[i]) { + strcat(buf, vpi_get_str(vpiName, net_handle)); + strcat(buf, " "); + vpi_get_value(net_handle, &value_s); + strcat(buf, value_s.value.str); + strcat(buf, " "); + changeFlag[i] = 0; + } + i++; + vpi_free_object(net_handle); // done with this one + } + //vpi_free_object(net_iter); + + n = write_pipe(buf, strlen(buf)); + if ((n = read_pipe(buf, MAXLINE)) <= 0) { + // vpi_printf("ABORT from RO cb\n"); + vpi_control(vpiFinish, 1); /* abort simulation */ + return(0); + } + assert(n > 0); + buf[n] = '\0'; + + + + /* save copy for later callback */ + strcpy(bufcp, buf); + + myhdl_time_string = strtok(buf, " "); + myhdl_time = (myhdl_time64_t) strtoull(myhdl_time_string, (char **) NULL, 10); + delay = (myhdl_time - pli_time) * 1000; + assert(delay >= 0); + assert(delay <= 0xFFFFFFFF); + if (delay > 0) { // schedule cbAfterDelay callback + assert(delay > delta); + delay -= delta; + /* Icarus 20030518 runs RO callbacks when time has already advanced */ + /* Therefore, one had to compensate for the prescheduled delta callback */ + /* delay -= 1; */ + /* Icarus 20031009 has a different scheduler, more correct I believe */ + /* compensation is no longer necessary */ + delta = 0; + pli_time = myhdl_time; + + // register cbAfterDelay callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = (PLI_UINT32) delay; + cb_data_s.reason = cbAfterDelay; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = delay_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + } else { + delta++; + assert(delta < 1000); + } + return(0); +} + +static PLI_INT32 delay_callback(p_cb_data cb_data) +{ + s_vpi_time time_s; + s_cb_data cb_data_s; + vpiHandle cb_h; + + // register readonly callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 0; + cb_data_s.reason = cbReadOnlySynch; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = readonly_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + // register delta callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 1; + cb_data_s.reason = cbAfterDelay; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = delta_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + return(0); +} + +static PLI_INT32 delta_callback(p_cb_data cb_data) +{ + s_cb_data cb_data_s; + s_vpi_time time_s; + vpiHandle reg_iter, reg_handle, cb_h; + s_vpi_value value_s; + + if (delta == 0) { + return(0); + } + + /* skip time value */ + strtok(bufcp, " "); + + reg_iter = vpi_iterate(vpiArgument, from_myhdl_systf_handle); + + value_s.format = vpiHexStrVal; + while ((value_s.value.str = strtok(NULL, " ")) != NULL) { + reg_handle = vpi_scan(reg_iter); + vpi_put_value(reg_handle, &value_s, NULL, vpiNoDelay); + vpi_free_object(reg_handle); + } + + if (reg_iter != NULL) { + vpi_free_object(reg_iter); + } + + // register readonly callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 0; + cb_data_s.reason = cbReadOnlySynch; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = readonly_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + // register delta callback // + time_s.type = vpiSimTime; + time_s.high = 0; + time_s.low = 1; + cb_data_s.reason = cbAfterDelay; + cb_data_s.user_data = NULL; + cb_data_s.cb_rtn = delta_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_h = vpi_register_cb(&cb_data_s); + vpi_free_object(cb_h); + + return(0); +} + +static PLI_INT32 change_callback(p_cb_data cb_data) +{ + int *id; + + // vpi_printf("change callback"); + id = (int *)cb_data->user_data; + changeFlag[*id] = 1; + return(0); +} + + +__declspec(dllexport) +void myhdl_register() +{ + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$to_myhdl"; + tf_data.calltf = (void *) to_myhdl_calltf; + tf_data.compiletf = NULL; + tf_data.sizetf = NULL; + tf_data.user_data = "$to_myhdl"; + vpi_register_systf(&tf_data); + //vpi_free_object(tf_data); // @mod cfelton + + tf_data.type = vpiSysTask; + tf_data.tfname = "$from_myhdl"; + tf_data.calltf = (void *) from_myhdl_calltf; + tf_data.compiletf = NULL; + tf_data.sizetf = NULL; + tf_data.user_data = "$from_myhdl"; + vpi_register_systf(&tf_data); + //vpi_free_object(tf_data); // @mod cfelton +} + +__declspec(dllexport) +void (*vlog_startup_routines[])() = { + myhdl_register, + 0 +}; + +/* dummy +loadvpi= boostrap routine - mimics old style exec all routines */ +/* in standard PLI vlog_startup_routines table */ +__declspec(dllexport) +void vpi_compat_bootstrap(void) +{ + int i; + + for (i = 0;; i++) + { + if (vlog_startup_routines[i] == NULL) break; + vlog_startup_routines[i](); + } +} diff --git a/cosimulation/modelsim-win/test/bin2gray.py b/cosimulation/modelsim-win/test/bin2gray.py new file mode 100644 index 00000000..d45d60cd --- /dev/null +++ b/cosimulation/modelsim-win/test/bin2gray.py @@ -0,0 +1,11 @@ +import os + +from myhdl import Cosimulation + +cmd = 'vsim -c -quiet -pli ../myhdl_vpi.dll -do cosim.do dut_bin2gray' + +def bin2gray(B, G, width): + os.system('vlog -quiet +define+width=%s ../../test/verilog/bin2gray.v' % (width)) + os.system('vlog -quiet +define+width=%s ../../test/verilog/dut_bin2gray.v' % (width)) + + return Cosimulation(cmd, B=B, G=G) diff --git a/cosimulation/modelsim-win/test/cosim.do b/cosimulation/modelsim-win/test/cosim.do new file mode 100644 index 00000000..99978978 --- /dev/null +++ b/cosimulation/modelsim-win/test/cosim.do @@ -0,0 +1,2 @@ +run -all +quit diff --git a/cosimulation/modelsim-win/test/dff.py b/cosimulation/modelsim-win/test/dff.py new file mode 100644 index 00000000..d7f19c4a --- /dev/null +++ b/cosimulation/modelsim-win/test/dff.py @@ -0,0 +1,11 @@ +import os + +from myhdl import Cosimulation + +cmd = 'vsim -c -quiet -pli ../myhdl_vpi.dll -do cosim.do dut_dff' + +def dff(q, d, clk, reset): + os.system('vlog -quiet ../../test/verilog/dff.v') + os.system('vlog -quiet ../../test/verilog/dut_dff.v') + + return Cosimulation(cmd, **locals()) diff --git a/cosimulation/modelsim-win/test/dff_clkout.py b/cosimulation/modelsim-win/test/dff_clkout.py new file mode 100644 index 00000000..248980f4 --- /dev/null +++ b/cosimulation/modelsim-win/test/dff_clkout.py @@ -0,0 +1,11 @@ +import os + +from myhdl import Cosimulation + +cmd = 'vsim -c -quiet -pli ../myhdl_vpi.dll -do cosim.do dut_dff_clkout' + +def dff_clkout(clkout, q, d, clk, reset): + os.system('vlog -quiet ../../test/verilog/dff_clkout.v') + os.system('vlog -quiet ../../test/verilog/dut_dff_clkout.v') + + return Cosimulation(cmd, **locals()) diff --git a/cosimulation/modelsim-win/test/inc.py b/cosimulation/modelsim-win/test/inc.py new file mode 100644 index 00000000..f6c418eb --- /dev/null +++ b/cosimulation/modelsim-win/test/inc.py @@ -0,0 +1,11 @@ +import os + +from myhdl import Cosimulation + +cmd = 'vsim -c -quiet -pli ../myhdl_vpi.dll -do cosim.do dut_inc' + +def inc(count, enable, clock, reset, n): + os.system('vlog -quiet +define+n=%s ../../test/verilog/inc.v' % (n)) + os.system('vlog -quiet +define+n=%s ../../test/verilog/dut_inc.v' % (n)) + + return Cosimulation(cmd, **locals()) diff --git a/cosimulation/modelsim-win/test/test_all.py b/cosimulation/modelsim-win/test/test_all.py new file mode 100644 index 00000000..136f35c9 --- /dev/null +++ b/cosimulation/modelsim-win/test/test_all.py @@ -0,0 +1,47 @@ +# This file is part of the myhdl library, a Python package for using +# Python as a Hardware Description Language. +# +# Copyright (C) 2003-2008 Jan Decaluwe +# +# The myhdl library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" Run cosimulation unit tests. """ + + +import sys + +sys.path.append("../../test") + +import test_bin2gray, test_inc, test_dff + +# modules = (test_dff, ) +modules = (test_bin2gray, test_inc, test_dff ) + +import unittest + +tl = unittest.defaultTestLoader +def suite(): + alltests = unittest.TestSuite() + for m in modules: + alltests.addTest(tl.loadTestsFromModule(m)) + return alltests + +def main(): + unittest.main(defaultTest='suite', + testRunner=unittest.TextTestRunner(verbosity=2)) + + +if __name__ == '__main__': + main() diff --git a/cosimulation/modelsim/Makefile b/cosimulation/modelsim/Makefile index fa21f85c..91727c13 100644 --- a/cosimulation/modelsim/Makefile +++ b/cosimulation/modelsim/Makefile @@ -1,33 +1,25 @@ -# could add to CFLAGS to turn on warnings if you are using gcc -WARNS=-Wall +# This makefile assumes that 32bit Modelsim is installed. +# If you have a 64 bit version, run 'ARCH=64 make' +ARCH?=32 +CFLAGS_32= -m32 +CFLAGS_64= -m64 -# change this path to point to the pli include files directory for cver +# Guess include dir based on location of vsim INCS=-I $(shell dirname `which vsim`)/../include +CFLAGS= -Wall -shared -BSymbolic -fPIC $(CFLAGS_$(ARCH)) $(INCS) -# maybe want -O and/or -g +VSIM_VERSION=$(shell vsim -version 2>/dev/null; echo $$?) -# 32bit for Altera ASE/PE on Ubuntu Natty Narwhal -CFLAGS= -fPIC -Wall -g -m32 $(INCS) -fno-stack-protector -LFLAGS= -G -shared -export-dynamic -melf_i386 - -# 64bit for SE -#CFLAGS= -fPIC -Wall -c -g $(INCS) -#LFLAGS= -shared -E - -# change to your compiler -CC=gcc all: myhdl_vpi.so -myhdl_vpi.o: myhdl_vpi.c - $(CC) $(CFLAGS) -c myhdl_vpi.c - -# make rules for dynamic libaries -myhdl_vpi.so: myhdl_vpi.o - $(LD) $(LFLAGS) myhdl_vpi.o -o myhdl_vpi.so +myhdl_vpi.so: myhdl_vpi.c + $(info Compiling $(ARCH)bit vpi lib for "$(VSIM_VERSION)") + $(info ) + $(CC) $(CFLAGS) -o $@ $? clean: - -rm *.o *.so + @rm -f myhdl_vpi.so .PHONY: test test: myhdl_vpi.so diff --git a/cosimulation/modelsim/myhdl_vpi.c b/cosimulation/modelsim/myhdl_vpi.c index 60ac9dce..54d3640c 100644 --- a/cosimulation/modelsim/myhdl_vpi.c +++ b/cosimulation/modelsim/myhdl_vpi.c @@ -125,7 +125,7 @@ static PLI_INT32 from_myhdl_calltf(PLI_BYTE8 *user_data) while ((reg_handle = vpi_scan(reg_iter)) != NULL) { if (vpi_get(vpiType, reg_handle) != vpiReg) { vpi_printf("ERROR: $from_myhdl argument %s should be a reg\n", - vpi_get_str(vpiName, reg_handle)); + vpi_get_str(vpiName, reg_handle)); vpi_control(vpiFinish, 1); /* abort simulation */ return(0); } @@ -137,7 +137,7 @@ static PLI_INT32 from_myhdl_calltf(PLI_BYTE8 *user_data) } //vpi_free_object(reg_iter); - n = write(wpipe, buf, strlen(buf)); + n = write(wpipe, buf, strlen(buf)); if ((n = read(rpipe, buf, MAXLINE)) == 0) { vpi_printf("Info: MyHDL simulator down\n"); @@ -274,12 +274,12 @@ static PLI_INT32 readonly_callback(p_cb_data cb_data) if (start_flag) { start_flag = 0; - n = write(wpipe, "START", 5); + n = write(wpipe, "START", 5); // vpi_printf("INFO: RO cb at start-up\n"); if ((n = read(rpipe, buf, MAXLINE)) == 0) { vpi_printf("ABORT from RO cb at start-up\n"); vpi_control(vpiFinish, 1); /* abort simulation */ - } + } assert(n > 0); } @@ -311,7 +311,7 @@ static PLI_INT32 readonly_callback(p_cb_data cb_data) vpi_free_object(net_handle); // done with this one } //vpi_free_object(net_iter); - + n = write(wpipe, buf, strlen(buf)); if ((n = read(rpipe, buf, MAXLINE)) == 0) { // vpi_printf("ABORT from RO cb\n"); @@ -463,7 +463,6 @@ static PLI_INT32 change_callback(p_cb_data cb_data) } - void myhdl_register() { s_vpi_systf_data tf_data; @@ -498,9 +497,9 @@ void vpi_compat_bootstrap(void) { int i; - for (i = 0;; i++) + for (i = 0;; i++) { - if (vlog_startup_routines[i] == NULL) break; + if (vlog_startup_routines[i] == NULL) break; vlog_startup_routines[i](); } } diff --git a/doc/source/conf.py b/doc/source/conf.py index 3948bcb6..2c5674ff 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -13,10 +13,12 @@ import sys, os + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../..')) +import myhdl # -- General configuration ----------------------------------------------------- @@ -42,16 +44,16 @@ master_doc = 'index' # General information about the project. project = u'MyHDL' -copyright = u'2014, Jan Decaluwe' +copyright = u'2015, Jan Decaluwe' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.8' +# version = myhdl.__version__ # The full version, including alpha/beta/rc tags. -release = '0.8' +release = myhdl.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/index.rst b/doc/source/index.rst index 30ff69d0..556edd3a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -19,9 +19,20 @@ Welcome to the MyHDL documentation manual/index whatsnew/0.9 + python3 + +Old Whatsnew documents +====================== +.. toctree:: + :maxdepth: 1 + whatsnew/0.8 whatsnew/0.7 whatsnew/0.6 + whatsnew/0.5 + whatsnew/0.4 + whatsnew/0.3 + Index ===== @@ -31,4 +42,3 @@ Index Search ====== * :ref:`search` - diff --git a/doc/source/manual/conversion.rst b/doc/source/manual/conversion.rst index 0e643008..3d1df941 100644 --- a/doc/source/manual/conversion.rst +++ b/doc/source/manual/conversion.rst @@ -73,15 +73,13 @@ Generator are mapped to Verilog or VHDL constructs it will map them to ``process`` statements or concurrent signal assignments. The module ports are inferred from signal usage - In MyHDL, the input or output direction of interface signals is not explicitly + In MyHDL, the input or output direction of ports is not explicitly declared. The converter investigates signal usage in the design hierarchy to infer whether a signal is used as input, output, or as an internal signal. -Interfaces are expanded - MyHDL provides a power mechanism to define complex module interfaces. - These interfaces are name extended to individual signals in the target - HDL (Verilog and VHDL). This enables MyHDL to support higher-level - abstractions that are not available in the target HDL. +Interfaces are convertible + An *interface*: an object that has a number of :class:`Signal` objects as its + attributes. The convertor supports this by name expansion and mangling. Function calls are mapped to Verilog or VHDL subprograms The converter analyzes function calls and function code. Each function is @@ -434,16 +432,15 @@ description of RAM memories. Conversion of Interfaces ======================== -Interfaces simplify the complicated interconnect between modules. -In MyHDL the interfaces provide an intuitive approach to group -logically related ports. The grouping of ports has many benefits -such as reducing complexity and increasing modularity. -The converter will name extend the interfaces during conversion, -in the converted code each attribute will appear as a individual -signal. Because the hierarchy is flattened the name extension -may need to include information on the where in the hierarchy -the interface occurs to prevent name collisions. +Complex designs often have many signals that are passed to different levels of +hierarchy. Typically, many signals logically belong together. This can be +modelled by an *interface*: an object that has a number of :class:`Signal` +objects as its attributes. Grouping signals into an interface simplifies the +code, improves efficiency, and reduces errors. + +The convertor supports interface using hierarchical name expansion and name +mangling. .. _conv-meth-assign: diff --git a/doc/source/manual/reference.rst b/doc/source/manual/reference.rst index e34b61d1..38ce3afa 100644 --- a/doc/source/manual/reference.rst +++ b/doc/source/manual/reference.rst @@ -85,8 +85,13 @@ Waveform tracing This attribute is used to overwrite the default top-level instance name and the basename of the VCD output filename. + .. attribute:: directory + + This attribute is used to set the directory to which VCD files are written. By + default, the current working directory is used. + .. attribute:: timescale - + This attribute is used to set the timescale corresponding to unit steps, according to the VCD format. The assigned value should be a string. The default timescale is "1ns". @@ -212,7 +217,7 @@ Shadow signals .. class:: _SliceSignal(sig, left[, right=None]) This class implements read-only structural slicing and indexing. It creates a new - signal that shadows the slice or index of the parent signal *sig*. If the + shadow signal of the slice or index of the parent signal *sig*. If the *right* parameter is omitted, you get indexing instead of slicing. Parameters *left* and *right* have the usual meaning for slice indices: in particular, *left* is non-inclusive but *right* @@ -229,11 +234,15 @@ Shadow signals .. class:: ConcatSignal(*args) - This class creates a new signal that shadows the concatenation - of its parent signal values. You can pass an arbitrary number - of signals to the constructor. The signal arguments should be bit-oriented - with a defined number of bits. + This class creates a new shadow signal of the concatenation of its arguments. + You can pass an arbitrary number of arguments to the constructor. The + arguments should be bit-oriented with a defined number of bits. The following + argument types are supported: :class:`intbv` objects with a defined bit width, + :class:`bool` objects, signals of the previous objects, and bit strings. + + The new signal follows the value changes of the signal arguments. The non-signal + arguments are used to define constant values in the concatenation. .. class:: TristateSignal(val) @@ -601,8 +610,10 @@ useful for hardware description. The following argument types are supported: :class:`intbv` objects with a defined bit width, :class:`bool` objects, signals of the previous objects, and - bit strings. All these objects have a defined bit width. The first argument - *base* is special as it doesn't need to have a defined bit width. In addition to + bit strings. All these objects have a defined bit width. + + The first argument *base* is special as it does not need to have a + defined bit width. In addition to the previously mentioned objects, unsized :class:`intbv`, :class:`int` and :class:`long` objects are supported, as well as signals of such objects. @@ -762,6 +773,13 @@ Conversion file. The assigned value should be a string. The default library is ``work``. + .. attribute:: std_logic_ports + + This boolean attribute can be used to have only ``std_logic`` type + ports on the top-level interface (when ``True``) instead of the + default ``signed/unsigned`` types (when ``False``, the default). + + .. _ref-conv-user: diff --git a/doc/source/python3.rst b/doc/source/python3.rst new file mode 100644 index 00000000..2b2f3c2e --- /dev/null +++ b/doc/source/python3.rst @@ -0,0 +1,8 @@ +Python 3 Support +================ + +MyHDL supports Python 3.4 and above. +At the moment, core functions, cosimulation and Verilog conversion work perfectly. +However, there are a few unresolved VHDL conversion bugs. + +All users are encouraged to try out their existing projects and tests with Python 3 and submit bug reports if anything goes wrong. diff --git a/doc/source/whatsnew/0.4.rst b/doc/source/whatsnew/0.4.rst index 392b928a..933fa4ec 100644 --- a/doc/source/whatsnew/0.4.rst +++ b/doc/source/whatsnew/0.4.rst @@ -1,6 +1,6 @@ -======================================= -New in MyHDL 0.4: Conversion to Verilog -======================================= +============================================== +What's new in MyHDL 0.4: Conversion to Verilog +============================================== :Author: Jan Decaluwe @@ -187,8 +187,11 @@ The following is a list of the statements that are supported by the Verilog converter, possibly qualified with restrictions or usage notes. The :keyword:`break` statement. + The :keyword:`continue` statement. + The :keyword:`def` statement. + The :keyword:`for` statement. The only supported iteration scheme is iterating through sequences of integers returned by built-in function :func:`range` or @@ -200,6 +203,7 @@ The :keyword:`if` statement. fully supported. The :keyword:`pass` statement. + The :keyword:`print` statement. When printing an interpolated string, the format specifiers are copied verbatim to the Verilog output. Printing to a file (with @@ -210,6 +214,7 @@ The :keyword:`raise` statement. simulation with an error message. The :keyword:`return` statement. + The :keyword:`yield` statement. The yielded expression can be a signal, a signal edge as specified by MyHDL functions :func:`posedge` or :func:`negedge`, or a tuple of diff --git a/doc/source/whatsnew/0.5.rst b/doc/source/whatsnew/0.5.rst new file mode 100644 index 00000000..a66d5128 --- /dev/null +++ b/doc/source/whatsnew/0.5.rst @@ -0,0 +1,587 @@ +.. currentmodule:: myhdl + +.. _new05: + +======================= +What's new in MyHDL 0.5 +======================= + +:Author: Jan Decaluwe + +Modeling +======== + +Creating generators with decorators +----------------------------------- + +Introduction +~~~~~~~~~~~~ + +Python 2.4 introduced a new feature called *decorators*. A decorator consists +of special syntax in front of a function declaration. It refers to a decorator +function. The decorator function automatically transforms the declared function +into some other callable object. + +MyHDL 0.5 defines decorators that can be used to create ready-to-run generators +from local functions. The use of decorators results in clearer, more explicit +code. + +The ``@instance`` decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``@instance`` decorator is the most general decorator in MyHDL. + +In earlier versions of MyHDL, local generator functions are typically used as +follows: + +:: + + def top(...): + ... + def gen_func(): + + ... + inst = gen_func() + ... + return inst, ... + +Note that the generator function :func:`gen_func()` is intended to be called +exactly once, and that its name is not necessary anymore afterwards. In MyHDL +0.5, this can be rewritten as follows, using the ``@instance`` decorator: + +:: + + def top(...): + ... + @instance + def inst(): + + ... + return inst, ... + + +Behind the curtains, the ``@instance`` decorator automatically creates a +generator by calling the generator function, and by reusing its name. Note that +it is placed immediately in front of the corresponding generator function, +resulting in clearer code. + +The ``@always`` decorator +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``@always`` decorator is a specialized decorator that targets a very +popular coding pattern. It is used as follows: + +:: + + def top(...): + ... + @always(event1, event2, ...) + def inst() + + ... + return inst, ... + +The meaning of this code is that the decorated function is executed whenever +one of the events occurs. The argument list of the decorator corresponds to the +sensitivity list. Appropriate events are edge specifiers, signals, and delay +objects. The decorated function is a classic function instead of a generator +function. + +Behind the curtains, the ``always`` decorator creates an enclosing ``while +True`` loop automatically, and inserts a ``yield`` statement with the +sensitivity list. + +The ``@always_comb`` decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``@always_comb`` decorator is used to describe combinatorial logic. It is +nothing else than the :func:`always_comb` function from earlier MyHDL versions +used as a decorator: + +:: + + def top(...): + ... + @always_comb + def comb_inst(): + + ... + return comb_inst, ... + +The ``@always_comb`` decorator infers the inputs of the combinatorial logic and +the corresponding sensitivity list automatically. + +More information +~~~~~~~~~~~~~~~~ + +For more information about the background and the design decisions regarding +MyHDL decorators, see `mep-100`_. + +Recommended style changes +------------------------- + +Decorator usage +~~~~~~~~~~~~~~~ + +The use of decorators has clear advantages in terms of code clarity. Therefore, +it is recommended that all local generators be created using decorators. + +Edge specifiers +~~~~~~~~~~~~~~~ + +Signal edges are typically specified using the :func:`posedge()` and +:func:`negedge()` functions in MyHDL. However, these functions are simply +wrappers around attributes with the same name. The design decision to use +functions have been reviewed and found questionable. In fact, using the +attributes directly instead has significant advantages, listed in order of +increasing significance: + + * one character less to type + * more object-oriented style + * less symbols in the ``myhdl`` namespace + * no brackets, which is better for clarity + * no function call overhead + +From MyHDL 0.5 on, it is therefore recommended to use the edge specifier +attributes. For example: + +:: + + clk.posedge # instead of posedge(clk) + rst.negedge # instead of negedge(clk) + +Deprecated features +------------------- + +Edge specifier functions +~~~~~~~~~~~~~~~~~~~~~~~~ + +Functions :func:`posedge()` and :func:`negedge()` are deprecated. As discussed +before, it is recommended to use the signal attributes with the same name +instead. + +In MyHDL 0.5, the functions will be removed from all documentation and +examples. They will be removed from MyHDL in a future version. + +processes() function +~~~~~~~~~~~~~~~~~~~~ + +Function :func:`processes()` is deprecated. It looks up local generator functions and +calls them to create generators. When MyHDL 0.5 decorators are used as +recommended, this functionality becomes superfluous as it is part of the +decorator functionality. + +On the other hand, the companion function :func:`instances()` continues to be +relevant and useful. It merely looks up instances in a local namespace. Having +a single lookup function will also improve usability. + +In MyHDL 0.5, the :func:`processes()` function will be removed from all documentation +and examples. It will be removed from MyHDL in a future version. + +Backwards incompatible changes +------------------------------ + +Default initial value of an intbv instance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It has always been possible to construct an :class:`intbv` instance without +explicit initial value: + +:: + + m = intbv() + +Prior to MyHDL 0.4, the default initial value was ``0``. In MyHDL 0.5, this has +been changed to ``None``. This is a first step towards support for ``X`` and +``Z`` functionality as found in other HDLs. This may be occasionally useful :-) +For example, it may be meaningful to initialize memory locations to ``None`` to +make sure that they will not be read before they have been initialized. If +``None`` is supported, it seems also logical to make it the default initial +value, to be interpreted as "No value". + +**Warning**: if you have calls like the above in your code, it will probably +fail with MyHDL 0.5, as many integer-like operations are not supported with +`None` values. + +**Workaround**: change your existing code by using ``0`` as an explicit initial +value, like so:: + + m = intbv(0) + +Python version +-------------- + +Because of the usage of new features such as decorators, MyHDL 0.5 requires +upgrading to Python 2.4 or higher. + +Verilog conversion +================== + +Decorator support +----------------- + +The Verilog convertor was enhanced to support the proposed decorators. + +Mapping a list of signals to a RAM memory +----------------------------------------- + +Certain synthesis tools can map Verilog memories to memory structures. For +example, this is supported by the Xilinx toolset. To support this interesting +feature, the Verilog convertor now maps lists of signals in MyHDL to Verilog +memories. + +The following MyHDL example is a ram model that uses a list of signals to model +the internal memory. + +:: + + def RAM(dout, din, addr, we, clk, depth=128): + """ Ram model """ + + mem = [Signal(intbv(0)[8:]) for i in range(depth)] + + @always(clk.posedge) + def write(): + if we: + mem[int(addr)].next = din + + @always_comb + def read(): + dout.next = mem[int(addr)] + + return write, read + +With the appropriate signal definitions for the interface ports, it is mapped +by :func:`toVerilog` to the following Verilog code. Note how the list of signals +``mem`` is mapped to a Verilog memory. + +:: + + module RAM ( + dout, + din, + addr, + we, + clk + ); + + output [7:0] dout; + wire [7:0] dout; + input [7:0] din; + input [6:0] addr; + input we; + input clk; + + reg [7:0] mem [0:128-1]; + + always @(posedge clk) begin: _RAM_write + if (we) begin + mem[addr] <= din; + end + end + + assign dout = mem[addr]; + + endmodule + +Lists of signals can also be used in MyHDL to elegantly describe iterative +hierarchical structures. (See the MyHDL manual.) However, there is an important +difference: such signals will have a name at some level of the hierarchy, while +in the case described above, the individual signals are anonymous. The +:func:`toVerilog` convertor detects which case we are in. In the first case, +the individual signals will still be declared in the Verilog output, using the +highest-level hierarchical name. It is only in the second case that the list of +signals is declared as a Verilog memory. + + +Mapping combinatorial logic to assign statements +------------------------------------------------ + +When possible, combinatorial logic is now converted to Verilog assign +statements. There are two conditions for this to happen. First, the logic has +to be explicitly described as a combinatorial function using the +``@always_comb`` decorator. Secondly, the function has to be simple enough so +that a mapping to assign statements is possible: only signal assignments are +permitted. Otherwise, a Verilog always block is used as previously. + +See the RAM model of the previous section for an example. + +This was done because certain synthesis tools require assign statements to +recognize code templates. + +Mapping a tuple of integers to a ROM memory +-------------------------------------------- + +Some synthesis tools, such as the Xilinx tool, can infer a ROM memory from a +case statement. :func:`toVerilog` has been enhanced to do the expansion into a +case statement automatically, based on a higher level description. The rom +access is described in a single line, by indexing into a tuple of integers. The +tuple can be described manually, but also by programmatical means. Note that a +tuple is used instead of a list to stress the read-only character of the +memory. + +The following example illustrates this functionality. + +:: + + def rom(dout, addr, CONTENT): + + @always_comb + def read(): + dout.next = CONTENT[int(addr)] + + return read + + dout = Signal(intbv(0)[8:]) + addr = Signal(intbv(0)[4:]) + CONTENT = (17, 134, 52, 9) + + toVerilog(rom, dout, addr, CONTENT) + +The output Verilog code is as follows: + +:: + + module rom ( + dout, + addr + ); + + output [7:0] dout; + reg [7:0] dout; + input [3:0] addr; + + always @(addr) begin: _rom_read + // synthesis parallel_case full_case + case (addr) + 0: dout <= 17; + 1: dout <= 134; + 2: dout <= 52; + default: dout <= 9; + endcase + end + + endmodule + +Support for signed arithmetic +----------------------------- + +Getting signed representations right in Verilog is tricky. One issue is that a +signed representation is treated as a special case, and unsigned as the rule. +For example, whenever one of the operands in an expression is unsigned, all +others are also treated like unsigned. While this is understandable from a +historical perspective (for backwards compatibility reasons) it is the opposite +from what one expects from a high-level point of view, when working with +negative numbers. The basic problem is that a Verilog user has to deal with +representation explicitly in all cases, even for abstract integer operations. +It would be much better to leave representational issues to a tool. + +MyHDL doesn't make the distinction between signed and unsigned. The +:class:`intbv` class can handle any kind of integer, including negative ones. +If required, you can access the 2's complement representation of an +:class:`intbv` object, but for integer operations such a counting, there is no +need to worry about this. + +Of course, the Verilog convertor has to deal with the representation carefully. +MyHDL 0.4 avoided the issue by simply prohibiting :class:`intbv` objects with +negative values. MyHDL 0.5 adds support for negative values and uses the signed +Verilog representation to accomplish this. + +The problematic cases are those when signed and unsigned representations are +mixed in Verilog expressions. The convertor avoids this by making sure that +signed arithmetic is used whenever one of the operands is signed. Note that +this is exactly the opposite of the Verilog default. More specifically, the +convertor may convert an unsigned operand by adding a sign bit and casting to a +signed interpretation, using the Verilog ``$signed`` function. Operands that +are treated like this are positive :class:`intbv` objects, slices and +subscripts of :class:`intbv` objects, and :class:`bool` objects. + +Integer constants are treated as a special case. Unsized integer numbers were +always treated as signed numbers in Verilog. However, as their representation +is minimally 32 bits wide, they usually don't give problems when mixed with +unsigned numbers. Therefore, integer constants don't cause signed casting of +other operands in the same expression: users would actually find it surprizing +if they did. + +Support for user-defined Verilog code +------------------------------------- + +Introduction +~~~~~~~~~~~~ + +In order to provide a path to implementation, MyHDL code can be converted to +Verilog. However, in some cases the conversion may fail or the result may not +be acceptable. For example: + +* conversion will fail if the MyHDL code doesn't follow the rules of the convertible subset +* a user may want to explicitly instantiate an existing Verilog module, instead of converting the corresponding MyHDL code +* it may be necessary to include technology-dependent modules in the Verilog output + +As a conclusion, MyHDL users need a method to include user-defined Verilog code +during the conversion process. + +Solution +~~~~~~~~ + +MyHDL 0.5 defines a hook that is understood by ``toVerilog`` but ignored by the +MyHDL simulator. The hook is called ``__verilog__``. Its operation can be +understood as a special return value. When a MyHDL function defines +``__verilog__``, the Verilog converter will use its value instead of the +regular return value. + +The value of ``__verilog__`` should be a format string that uses keys in its +format specifiers. The keys refer to the variable names in the context of the +string. + +Example:: + + def inc_comb(nextCount, count, n): + + @always_comb + def logic(): + nextCount.next = (count + 1) % n + + __verilog__ = \ + """ + assign %(nextCount)s = (%(count)s + 1) %% %(n)s; + """ + nextCount.driven = "wire" + + return logic + +In this example, conversion of the ``inc_comb`` function is bypassed and the +user-defined Verilog code is inserted instead. Note that the user-defined code +refers to signals and parameters in the MyHDL context by using format +specifiers. During conversion, the appropriate hierarchical names and parameter +values will be filled in. Note also that the format specifier indicator `%` +needs to be escaped (by doubling it) if it is required in the user-defined +code. + +There is one more issue that needs user attention. Normally, the Verilog +convertor infers inputs, internal signals, and outputs. It also detects +undriven and multiple driven signals. To do this, it assumes that signals are +not driven by default. It then processes the code to find out which signals are +driven from where. However, it cannot do this for user-defined code. Without +additional help, this will result in warnings or errors during the inference +process, or in compilation errors from invalid Verilog code. The user should +solve this by setting the ``driven`` attribute for signals that are driven from +the user-defined code. In the example code above, note the following +assignment:: + + nextCount.driven = "wire" + +This specifies that the ``nextCount`` signal is driven as a Verilog wire from +this module. The allowed values of the ``driven`` attribute are ``"wire"`` and +``"reg"``. The value specifies how the user-defined Verilog code drives the +signal in Verilog. To decide which value to use, consider how the signal should +be declared in Verilog after the user-defined code is inserted. + +Limitations +~~~~~~~~~~~ + +It is not possible to use the ``__verilog__`` hook in a generator function - +it should be in a classic function. This is because in MyHDL those functions +are completely run (elaborated) before the conversion starts, while generator +functions are not. + +More info +~~~~~~~~~ + +For more information about the background and the design decisions regarding +user-defined Verilog code, see `mep-101`_. + +Backwards incompatible changes +------------------------------ + +Verilog conversion output filename +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A Verilog conversion is performed with a call that looks as follows:: + + instance_name = toVerilog(func, ...) + +In MyHDL 0.4, the Verilog output filename was called ``instance_name.v``. In +MyHDL 0.5, the default output filename is ``func_name.v``, where ``func_name`` +is the name of the function, available as the ``func.func_name`` attribute. + +This was done for the following reasons. The MyHDL 0.4 was overly clever and +therefore complicated. It involves frame handling and parsing the source file +for the assignment pattern. Besides being too clever, it also had awkward +limitations. For example, it was not possible to construct a dynamic name for +the instance, which is very un-Pythonic behavior. + +Both the implementation complexity and the limitations are gone with the new +behavior: the name of the top-level function argument is simply used. In +addition, it is now possible to specify a user-defined name for the instance as +follows:: + + toVerilog.name = "my_name" + toVerilog(func, ....) + +To support this feature, it was necessary to make toVerilog an instance of a +class with a call interface. + +**Warning**: When existing converting code is re-run, the Verilog output +filename will be different than in 0.4. + +Simulation +========== + +Performance optimizations +------------------------- + +To improve the simulation performance of MyHDL, we mainly put our trust in +Python development itself. There are good reasons to be optimistic. + +What MyHDL itself can do is to minimize the overhead of the simulation loop. In +MyHDL 0.5, a first step was taken in this respect. + +MyHDL supports a number of "trigger objects". These are the objects that can +occur in ``yield`` statements, for example :class:`delay`, ``posedge``, +:class:`Signal`, and generator objects. Each of these are handled differently +and so the simulation loop has to account for the object type. Prior to MyHDL +0.5, this type check was explicitly done for each occurence of a ``yield`` +statement during simulation. As many generators will loop endlessly, it is +clear that the same things will be checked over and over again, resulting in an +important overhead. + +In MyHDL 0.5, all generators are predigested. Certain trigger object patterns +that tend to occur often are given specialized simulation handlers, so that +continuously performing the same type check is avoided. More specifically, they +consist of generators that only contain ``yield`` statements with a specific +argument. Currently, 5 different kinds of generators are recognized and +accelerated, corresponding to the following ``yield`` statement arguments: + + * a :class:`delay` object + * a :class:`Signal` object + * a tuple of :class:`Signal` objects + * a ``posedge`` or ``negedge`` object + * a tuple of ``posedge`` and/or ``negedge`` objects + +Backwards incompatible changes +------------------------------ + +Waveform tracing output filename +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Waveform tracing is initiated by a call that looks as follows:: + + instance_name = traceSignals(func, ...) + +In MyHDL 0.4, the output filename was called `instance_name.vcd`. In MyHDL 0.5, +the default output filename is `func_name.vcd`, where `func_name` is the name +of the function, available as the `func.func_name` attribute. + +This was done for the same reasons as in the similar case for `toVerilog`, as +described earlier. + +A user-defined name for the output file can be specified as follows:: + + traceSignals.name = "my_name" + inst = traceSignals(func, ....) + +**Warning**: When existing converting code is re-run, the vcd output filename +will be different than in 0.4. + +.. _mep-100: http://dev.myhdl.org/meps/mep-100.html +.. _mep-101: http://dev.myhdl.org/meps/mep-101.html diff --git a/doc/source/whatsnew/0.9.rst b/doc/source/whatsnew/0.9.rst index 3207f6f3..ed7e7a87 100644 --- a/doc/source/whatsnew/0.9.rst +++ b/doc/source/whatsnew/0.9.rst @@ -6,22 +6,26 @@ What's new in MyHDL 0.9 *********************** -Interfaces -=========== +Python 3 support +================ + +Experimental Python 3 support has been added to MyHDL 0.9. +This was a major effort to modernize the code. As a result, +Python 2 and 3 are supported from a single codebase. + +See :doc:`/python3` for more info. + +Interfaces (Conversion of attribute accesses) +============================================= Rationale --------- -Complex designs often have many signals (ports) that are passed to -different levels of hierarchy. Typically, many of the signals can -logically be grouped together. Grouping the signals into an -*interface* simplifies the code, improves efficiency, and reduces -errors. -An *interface* is a collection of signals (:class:`Signal`) embedded -in an class/object as attributes. This provides a natural method -to group related signals and provides intuitive access through -attributes. Multiple level of objects and attibutes provides a -hierarchy of signal structure if desired. +Complex designs often have many signals that are passed to different levels of +hierarchy. Typically, many signals logically belong together. This can be +modelled by an *interface*: an object that has a number of :class:`Signal` +objects as its attributes. Grouping signals into an interface simplifies the +code, improves efficiency, and reduces errors. The following is an example of an *interface* definition:: @@ -31,18 +35,10 @@ The following is an example of an *interface* definition:: self.imag = Signal(intbv(0, min=min, max=max)) -Previous versions supported *interfaces* for modeling and for -conversion if the attributes were locally referenced in a MyHDL -module outside of the `MyHDL generator`_. If the attributes were -directly referenced in the `MyHDL generator`_ the code would not be -convertible. +Although previous versions supported interfaces for modeling, they were not +convertible. MyHDL 0.9 now supports conversion of designs that use interfaces. -This features adds the ability to convert attributes that are -:class:`Signal` and referenced in the `MyHDL generator`_. This is -an evolution of a useful construct. - -The following -is an example using the above ``Complex`` interface definition:: +The following is an example using the above ``Complex`` interface definition:: a,b = Complex(-8,8), Complex(-8,8) c = Complex(-128,128) @@ -59,34 +55,18 @@ is an example using the above ``Complex`` interface definition:: Solution -------- -The proposed solution is to create unique names for attributes which -are type :class:`Signal` and used by a `MyHDL generator`_. The -converter will create a unique name by using the name of the parent -and the name of the attribute along with the name of the MyHDL module -instance (if required for uniqueness). The converter will essentially -replace the "." with an "_" for each *interface* element. - -Even though the target HDLs do not support *interfaces*, MyHDL is -able to add high-level features that compile during conversion to the -target HDL (Verilog and VHDL). - - -Conversion ----------- - -.. add details of the conversion, what policies are used to name -.. extend the Signals. Any useful information about the approach -.. or structure in the converter used. - - -Limitations ------------ -The current implementation only converts ``Signal`` attributes and -constants (read-only ints). Other Python structures will not be -analyzed (e.g. dict) and attributes used as variables will not be -converted. +The proposed solution is to create unique names for attributes which are used +by `MyHDL generator`_\s. The converter will create a unique name by using the +name of the parent and the name of the attribute along with the name of the +MyHDL module instance. The converter will essentially replace the "." with an +"_" for each interface element. In essence, interfaces are supported +using hierarchical name expansion and name mangling. +Note that the MyHDL convertor supports interfaces, even though +the target HDLs do not. This is another great example where the +convertor supports a high-level feature that is not available +in the target HDLs. See also -------- @@ -94,3 +74,41 @@ For additional information see the original proposal `mep-107`_. .. _mep-107: http://dev.myhdl.org/meps/mep-107.html .. _MyHDL generator: http://docs.myhdl.org/en/latest/manual/reference.html#myhdl-generators-and-trigger-objects + +Other noteworthy improvements +============================= + +``ConcatSignal`` interface +-------------------------- + +The interface of :class:`ConcatSignal` was enhanced. In +addition to signals, you can now also use constant values +in the concatenation. + +``std_logic`` type ports +------------------------ + +:func:`toVHDL` has a new attibute ``std_logic_ports``. When +set, only ``std_logic`` type ports are used in the interface +of the top-level VHDL module. + +Development flow +---------------- + +The MyHDL development flow has been modernized by moving to git and `github`_ +for version control. In addition, travis has set up so that all pull requests +are tested automatically, enabling continuous intergration. + +Acknowledgments +=============== + +The Python 3 support effort was coordinated by Keerthan Jaic, who also +implemented most of if. Convertible interfaces were championed by Chris Felton, +and implemented by Keerthan Jaic. + +MyHDL development is a collaborative effort, as can be seen on `github`_. +Thanks to all who contributed with suggestions, issues and pull requests. + +.. _github: https://www.github.com/jandecaluwe/myhdl + + diff --git a/example/cookbook/bitonic/bitonic.py b/example/cookbook/bitonic/bitonic.py index 3ccf8fab..f8d84fcf 100644 --- a/example/cookbook/bitonic/bitonic.py +++ b/example/cookbook/bitonic/bitonic.py @@ -1,3 +1,5 @@ +import subprocess + from myhdl import * from myhdl.conversion import analyze @@ -71,12 +73,15 @@ def Array8Sorter(a0, a1, a2, a3, a4, a5, a6, a7, def Array8Sorter_v(a0, a1, a2, a3, a4, a5, a6, a7, z0, z1, z2, z3, z4, z5, z6, z7): + analyze.simulator = 'iverilog' toVerilog(Array8Sorter, a0, a1, a2, a3, a4, a5, a6, a7, z0, z1, z2, z3, z4, z5, z6, z7) analyze(Array8Sorter, a0, a1, a2, a3, a4, a5, a6, a7, z0, z1, z2, z3, z4, z5, z6, z7) - cmd = "cver -q +loadvpi=../../../cosimulation/cver/myhdl_vpi:vpi_compat_bootstrap " + \ - "Array8Sorter.v tb_Array8Sorter.v" + # cmd = "cver -q +loadvpi=../../../cosimulation/cver/myhdl_vpi:vpi_compat_bootstrap " + \ + # "Array8Sorter.v tb_Array8Sorter.v" + subprocess.call("iverilog -o Array8Sorter.o Array8Sorter.v tb_Array8Sorter.v", shell=True) + cmd = "vvp -m ../../../cosimulation/icarus/myhdl.vpi Array8Sorter.o" return Cosimulation(cmd, **locals()) diff --git a/myhdl/_Cosimulation.py b/myhdl/_Cosimulation.py index 184901f3..afe7522d 100644 --- a/myhdl/_Cosimulation.py +++ b/myhdl/_Cosimulation.py @@ -20,17 +20,18 @@ """ Module that provides the Cosimulation class """ from __future__ import absolute_import - +import sys import os import shlex import subprocess from myhdl._intbv import intbv from myhdl import _simulator, CosimulationError -from myhdl._compat import PY2, string_types, to_bytes, to_str +from myhdl._compat import set_inheritable, string_types, to_bytes, to_str _MAXLINE = 4096 + class _error: pass _error.MultipleCosim = "Only a single cosimulator allowed" @@ -41,25 +42,32 @@ _error.NoCommunication = "No signals communicating to myhdl" _error.SimulationEnd = "Premature simulation end" _error.OSError = "OSError" + class Cosimulation(object): """ Cosimulation class. """ def __init__(self, exe="", **kwargs): - + """ Construct a cosimulation object. """ - + if _simulator._cosim: raise CosimulationError(_error.MultipleCosim) _simulator._cosim = 1 - - self._rt, self._wt = rt, wt = os.pipe() - self._rf, self._wf = rf, wf = os.pipe() - # New pipes are not inheritable by default since py 3.4 - if not PY2: - for p in rt, wt, rf, wf: - os.set_inheritable(p, True) + rt, wt = os.pipe() + rf, wf = os.pipe() + + # Disable inheritance for ends that we don't want the child to have + set_inheritable(rt, False) + set_inheritable(wf, False) + + # Enable inheritance for child ends + set_inheritable(wt, True) + set_inheritable(rf, True) + + self._rt = rt + self._wf = wf self._fromSignames = fromSignames = [] self._fromSizes = fromSizes = [] @@ -71,20 +79,23 @@ class Cosimulation(object): self._hasChange = 0 self._getMode = 1 - def close_rt_wf(): - os.close(rt) - os.close(wf) - env = os.environ.copy() - env['MYHDL_TO_PIPE'] = str(wt) - env['MYHDL_FROM_PIPE'] = str(rf) + + # In Windows the FDs aren't inheritable when using Popen, + # only the HANDLEs are + if sys.platform != "win32": + env['MYHDL_TO_PIPE'] = str(wt) + env['MYHDL_FROM_PIPE'] = str(rf) + else: + import msvcrt + env['MYHDL_TO_PIPE'] = str(msvcrt.get_osfhandle(wt)) + env['MYHDL_FROM_PIPE'] = str(msvcrt.get_osfhandle(rf)) if isinstance(exe, string_types): exe = shlex.split(exe) - + try: - sp = subprocess.Popen(exe, env=env, close_fds=False, - preexec_fn=close_rt_wf) + sp = subprocess.Popen(exe, env=env, close_fds=False) except OSError as e: raise CosimulationError(_error.OSError, str(e)) @@ -154,7 +165,7 @@ class Cosimulation(object): except ValueError: next = intbv(0) s.next = next - + self._getMode = 0 def _put(self, time): @@ -182,7 +193,7 @@ class Cosimulation(object): while 1: yield sigs self._hasChange = 1 - + def __del__(self): """ Clear flag when this object destroyed - to suite unittest. """ _simulator._cosim = 0 diff --git a/myhdl/_ShadowSignal.py b/myhdl/_ShadowSignal.py index 683afc02..180ec30d 100644 --- a/myhdl/_ShadowSignal.py +++ b/myhdl/_ShadowSignal.py @@ -1,7 +1,7 @@ # This file is part of the myhdl library, a Python package for using # Python as a Hardware Description Language. # -# Copyright (C) 2003-2011 Jan Decaluwe +# Copyright (C) 2003-2015 Jan Decaluwe # # The myhdl library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as @@ -172,8 +172,12 @@ class ConcatSignal(_ShadowSignal): else: w = len(a) lo = hi - w - if a in sigargs: - newval[hi:lo] = a + # note: 'a in sigargs' is equivalence check, not identity + if isinstance(a, _Signal): + if isinstance(a._val, intbv): + newval[hi:lo] = a[w:] + else: + newval[hi:lo] = a hi = lo set_next(self, newval) yield sigargs diff --git a/myhdl/_Signal.py b/myhdl/_Signal.py index 81fd32ff..648c0214 100644 --- a/myhdl/_Signal.py +++ b/myhdl/_Signal.py @@ -29,7 +29,6 @@ negedge -- callable to model a falling edge on a signal in a yield statement from __future__ import absolute_import from __future__ import print_function -from inspect import currentframe, getouterframes from copy import copy, deepcopy import operator @@ -43,7 +42,7 @@ from myhdl._bin import bin _schedule = _futureEvents.append - + def _isListOfSigs(obj): """ Check if obj is a non-empty list of signals. """ if isinstance(obj, list) and len(obj) > 0: @@ -53,8 +52,8 @@ def _isListOfSigs(obj): return True else: return False - - + + class _WaiterList(list): def purge(self): @@ -69,7 +68,7 @@ class _PosedgeWaiterList(_WaiterList): return "posedge %s" % self.sig._name def _toVHDL(self): return "rising_edge(%s)" % self.sig._name - + class _NegedgeWaiterList(_WaiterList): def __init__(self, sig): self.sig = sig @@ -96,7 +95,7 @@ def Signal(val=None, delay=None): return _DelayedSignal(val, delay) else: return _Signal(val) - + class _Signal(object): """ _Signal class. @@ -109,8 +108,8 @@ class _Signal(object): __slots__ = ('_next', '_val', '_min', '_max', '_type', '_init', '_eventWaiters', '_posedgeWaiters', '_negedgeWaiters', - '_code', '_tracing', '_nrbits', '_checkVal', - '_setNextVal', '_copyVal2Next', '_printVcd', + '_code', '_tracing', '_nrbits', '_checkVal', + '_setNextVal', '_copyVal2Next', '_printVcd', '_driven' ,'_read', '_name', '_used', '_inList', '_waiter', 'toVHDL', 'toVerilog', '_slicesigs', '_numeric' @@ -121,7 +120,7 @@ class _Signal(object): """ Construct a signal. val -- initial value - + """ self._init = deepcopy(val) self._val = deepcopy(val) @@ -177,7 +176,7 @@ class _Signal(object): self._numeric = True for s in self._slicesigs: s._clear() - + def _update(self): val, next = self._val, self._next if val != next: @@ -227,12 +226,12 @@ class _Signal(object): @property def posedge(self): return self._posedgeWaiters - + # support for the 'negedge' attribute @property def negedge(self): return self._negedgeWaiters - + # support for the 'min' and 'max' attribute @property def max(self): @@ -252,7 +251,7 @@ class _Signal(object): if not val in ("reg", "wire", True): raise ValueError('Expected value "reg", "wire", or True, got "%s"' % val) self._driven = val - + # support for the 'read' attribute @property def read(self): @@ -297,17 +296,17 @@ class _Signal(object): def _setNextNonmutable(self, val): if not isinstance(val, self._type): raise TypeError("Expected %s, got %s" % (self._type, type(val))) - self._next = val - + self._next = val + def _setNextMutable(self, val): if not isinstance(val, self._type): raise TypeError("Expected %s, got %s" % (self._type, type(val))) - self._next = deepcopy(val) + self._next = deepcopy(val) # vcd print methods def _printVcdStr(self): print("s%s %s" % (str(self._val), self._code), file=sim._tf) - + def _printVcdHex(self): if self._val is None: print("sz %s" % self._code, file=sim._tf) @@ -334,11 +333,11 @@ class _Signal(object): ### operators for which delegation to current value is appropriate ### - + def __hash__(self): raise TypeError("Signals are unhashable") - - + + def __bool__(self): return bool(self._val) @@ -353,7 +352,7 @@ class _Signal(object): def __getitem__(self, key): return self._val[key] - + # integer-like methods def __add__(self, other): @@ -363,7 +362,7 @@ class _Signal(object): return self._val + other def __radd__(self, other): return other + self._val - + def __sub__(self, other): if isinstance(other, _Signal): return self._val - other._val @@ -387,7 +386,7 @@ class _Signal(object): return self._val / other def __rtruediv__(self, other): return other / self._val - + def __floordiv__(self, other): if isinstance(other, _Signal): return self._val // other._val @@ -395,7 +394,7 @@ class _Signal(object): return self._val // other def __rfloordiv__(self, other): return other // self._val - + def __mod__(self, other): if isinstance(other, _Signal): return self._val % other._val @@ -405,7 +404,7 @@ class _Signal(object): return other % self._val # XXX divmod - + def __pow__(self, other): if isinstance(other, _Signal): return self._val ** other._val @@ -421,7 +420,7 @@ class _Signal(object): return self._val << other def __rlshift__(self, other): return other << self._val - + def __rshift__(self, other): if isinstance(other, _Signal): return self._val >> other._val @@ -429,7 +428,7 @@ class _Signal(object): return self._val >> other def __rrshift__(self, other): return other >> self._val - + def __and__(self, other): if isinstance(other, _Signal): return self._val & other._val @@ -445,7 +444,7 @@ class _Signal(object): return self._val | other def __ror__(self, other): return other | self._val - + def __xor__(self, other): if isinstance(other, _Signal): return self._val ^ other._val @@ -453,7 +452,7 @@ class _Signal(object): return self._val ^ other def __rxor__(self, other): return other ^ self._val - + def __neg__(self): return -self._val @@ -465,33 +464,33 @@ class _Signal(object): def __invert__(self): return ~self._val - + # conversions - + def __int__(self): return int(self._val) - + def __long__(self): return long(self._val) def __float__(self): return float(self._val) - + def __oct__(self): return oct(self._val) - + def __hex__(self): return hex(self._val) - + def __index__(self): return int(self._val) # comparisons def __eq__(self, other): - return self.val == other + return self.val == other def __ne__(self, other): - return self.val != other + return self.val != other def __lt__(self, other): return self.val < other def __le__(self, other): @@ -506,7 +505,7 @@ class _Signal(object): def __getattr__(self, attr): return getattr(self._val, attr) - # representation + # representation def __str__(self): if self._name: return self._name @@ -556,7 +555,7 @@ class _Signal(object): class _DelayedSignal(_Signal): - + __slots__ = ('_nextZ', '_delay', '_timeStamp', ) @@ -594,7 +593,7 @@ class _DelayedSignal(_Signal): self._val = copy(next) if self._tracing: self._printVcd() - return waiters + return waiters else: return [] @@ -607,7 +606,7 @@ class _DelayedSignal(_Signal): def delay(self, delay): self._delay = delay - + class _SignalWrap(object): def __init__(self, sig, next, timeStamp): self.sig = sig diff --git a/myhdl/__init__.py b/myhdl/__init__.py index 8b4f7479..5da14d1b 100644 --- a/myhdl/__init__.py +++ b/myhdl/__init__.py @@ -1,7 +1,7 @@ # This file is part of the myhdl library, a Python package for using # Python as a Hardware Description Language. # -# Copyright (C) 2003-2013 Jan Decaluwe +# Copyright (C) 2003-2015 Jan Decaluwe # # The myhdl library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as @@ -52,7 +52,7 @@ toVerilog -- function that converts a design to Verilog from __future__ import absolute_import from __future__ import print_function -__version__ = "0.9.dev0" +__version__ = "1.0dev" import sys import warnings diff --git a/myhdl/_always.py b/myhdl/_always.py index 1dbe033d..9f8fb379 100644 --- a/myhdl/_always.py +++ b/myhdl/_always.py @@ -24,7 +24,7 @@ from __future__ import absolute_import from types import FunctionType from myhdl import AlwaysError -from myhdl._util import _isGenFunc +from myhdl._util import _isGenFunc, _makeAST from myhdl._delay import delay from myhdl._Signal import _Signal, _WaiterList, posedge, negedge from myhdl._Waiter import _Waiter, _SignalWaiter, _SignalTupleWaiter, \ @@ -58,28 +58,31 @@ def always(*args): raise AlwaysError(_error.NrOfArgs) return _Always(func, args) return _always_decorator - + class _Always(_Instantiator): - def __init__(self, func, args): + def __init__(self, func, senslist): self.func = func - self.senslist = tuple(args) - self.gen = self.genfunc() - + self.senslist = tuple(senslist) + super(_Always, self).__init__(self.genfunc) + + @property + def funcobj(self): + return self.func + + def _waiter(self): # infer appropriate waiter class # first infer base type of arguments for t in (_Signal, _WaiterList, delay): - if isinstance(args[0], t): + if isinstance(self.senslist[0], t): bt = t - for arg in args[1:]: - if not isinstance(arg, bt): + for s in self.senslist[1:]: + if not isinstance(s, bt): bt = None break # now set waiter class - W = _Waiter - if bt is delay: W = _DelayWaiter elif len(self.senslist) == 1: @@ -92,9 +95,7 @@ class _Always(_Instantiator): W = _SignalTupleWaiter elif bt is _WaiterList: W = _EdgeTupleWaiter - - self.waiter = W(self.gen) - + return W def genfunc(self): senslist = self.senslist @@ -104,4 +105,3 @@ class _Always(_Instantiator): while 1: yield senslist func() - diff --git a/myhdl/_always_comb.py b/myhdl/_always_comb.py index 3bfbefcf..fffb4768 100644 --- a/myhdl/_always_comb.py +++ b/myhdl/_always_comb.py @@ -29,10 +29,9 @@ import ast from myhdl import AlwaysCombError from myhdl._Signal import _Signal, _isListOfSigs from myhdl._util import _isGenFunc, _dedent -from myhdl._cell_deref import _cell_deref from myhdl._Waiter import _Waiter, _SignalWaiter, _SignalTupleWaiter from myhdl._instance import _Instantiator -from myhdl._resolverefs import _AttrRefTransformer +from myhdl._always import _Always class _error: pass @@ -50,117 +49,12 @@ def always_comb(func): raise AlwaysCombError(_error.ArgType) if func.__code__.co_argcount > 0: raise AlwaysCombError(_error.NrOfArgs) - varnames = func.__code__.co_varnames - symdict = {} - for n, v in func.__globals__.items(): - if n not in varnames: - symdict[n] = v - # handle free variables - if func.__code__.co_freevars: - for n, c in zip(func.__code__.co_freevars, func.__closure__): - try: - obj = _cell_deref(c) - symdict[n] = obj - except NameError: - raise NameError(n) - c = _AlwaysComb(func, symdict) + c = _AlwaysComb(func) return c -INPUT, OUTPUT, INOUT = range(3) - - - -class _SigNameVisitor(ast.NodeVisitor): - def __init__(self, symdict): - self.inputs = set() - self.outputs = set() - self.toplevel = 1 - self.symdict = symdict - self.context = INPUT - - def visit_Module(self, node): - inputs = self.inputs - outputs = self.outputs - for n in node.body: - self.visit(n) - for n in inputs: - if n in outputs: - raise AlwaysCombError(_error.SignalAsInout % n) - - def visit_FunctionDef(self, node): - if self.toplevel: - self.toplevel = 0 # skip embedded functions - for n in node.body: - self.visit(n) - else: - raise AlwaysCombError(_error.EmbeddedFunction) - - def visit_If(self, node): - if not node.orelse: - if isinstance(node.test, ast.Name) and \ - node.test.id == '__debug__': - return # skip - self.generic_visit(node) - - def visit_Name(self, node): - id = node.id - if id not in self.symdict: - return - s = self.symdict[id] - if isinstance(s, _Signal) or _isListOfSigs(s): - if self.context == INPUT: - self.inputs.add(id) - elif self.context == OUTPUT: - self.outputs.add(id) - elif self.context == INOUT: - raise AlwaysCombError(_error.SignalAsInout % id) - else: - raise AssertionError("bug in always_comb") - - def visit_Assign(self, node): - self.context = OUTPUT - for n in node.targets: - self.visit(n) - self.context = INPUT - self.visit(node.value) - - def visit_Attribute(self, node): - self.visit(node.value) - - def visit_Call(self, node): - fn = None - if isinstance(node.func, ast.Name): - fn = node.func.id - if fn == "len": - pass - else: - self.generic_visit(node) - - - def visit_Subscript(self, node, access=INPUT): - self.visit(node.value) - self.context = INPUT - self.visit(node.slice) - - def visit_AugAssign(self, node, access=INPUT): - self.context = INOUT - self.visit(node.target) - self.context = INPUT - self.visit(node.value) - - def visit_ClassDef(self, node): - pass # skip - - def visit_Exec(self, node): - pass # skip - - def visit_Print(self, node): - pass # skip - - - -class _AlwaysComb(_Instantiator): +# class _AlwaysComb(_Instantiator): +class _AlwaysComb(_Always): # def __init__(self, func, symdict): # self.func = func @@ -191,37 +85,26 @@ class _AlwaysComb(_Instantiator): # W = _SignalTupleWaiter # self.waiter = W(self.gen) - def __init__(self, func, symdict): - self.func = func - self.symdict = symdict - s = inspect.getsource(func) - s = _dedent(s) - tree = ast.parse(s) - # print ast.dump(tree) - v = _AttrRefTransformer(self) - v.visit(tree) - v = _SigNameVisitor(self.symdict) - v.visit(tree) - self.inputs = v.inputs - self.outputs = v.outputs + def __init__(self, func): senslist = [] + super(_AlwaysComb, self).__init__(func, senslist) + + inouts = self.inouts | self.inputs.intersection(self.outputs) + if inouts: + raise AlwaysCombError(_error.SignalAsInout % inouts) + + if self.embedded_func: + raise AlwaysCombError(_error.EmbeddedFunction) + for n in self.inputs: s = self.symdict[n] if isinstance(s, _Signal): senslist.append(s) - else: # list of sigs + elif _isListOfSigs(s): senslist.extend(s) self.senslist = tuple(senslist) - self.gen = self.genfunc() if len(self.senslist) == 0: raise AlwaysCombError(_error.EmptySensitivityList) - if len(self.senslist) == 1: - W = _SignalWaiter - else: - W = _SignalTupleWaiter - self.waiter = W(self.gen) - - def genfunc(self): senslist = self.senslist @@ -231,7 +114,3 @@ class _AlwaysComb(_Instantiator): while 1: func() yield senslist - - - - diff --git a/myhdl/_always_seq.py b/myhdl/_always_seq.py index bd6f87b7..94354098 100644 --- a/myhdl/_always_seq.py +++ b/myhdl/_always_seq.py @@ -28,12 +28,10 @@ import ast from myhdl import AlwaysError, intbv from myhdl._util import _isGenFunc, _dedent -from myhdl._cell_deref import _cell_deref from myhdl._delay import delay from myhdl._Signal import _Signal, _WaiterList,_isListOfSigs from myhdl._Waiter import _Waiter, _EdgeWaiter, _EdgeTupleWaiter -from myhdl._instance import _Instantiator -from myhdl._resolverefs import _AttrRefTransformer +from myhdl._always import _Always # evacuate this later AlwaysSeqError = AlwaysError @@ -82,13 +80,13 @@ def always_seq(edge, reset): return _always_seq_decorator -class _AlwaysSeq(_Instantiator): +class _AlwaysSeq(_Always): def __init__(self, func, edge, reset): - self.func = func - self.senslist = senslist = [edge] + senslist = [edge] self.reset = reset if reset is not None: + self.genfunc = self.genfunc_reset active = self.reset.active async = self.reset.async if async: @@ -96,44 +94,20 @@ class _AlwaysSeq(_Instantiator): senslist.append(reset.posedge) else: senslist.append(reset.negedge) - self.gen = self.genfunc() else: - self.gen = self.genfunc_no_reset() - if len(self.senslist) == 1: - W = _EdgeWaiter - else: - W = _EdgeTupleWaiter - self.waiter = W(self.gen) + self.genfunc = self.genfunc_no_reset - # find symdict - # similar to always_comb, but in class constructor - varnames = func.__code__.co_varnames - symdict = {} - for n, v in func.__globals__.items(): - if n not in varnames: - symdict[n] = v - # handle free variables - if func.__code__.co_freevars: - for n, c in zip(func.__code__.co_freevars, func.__closure__): - try: - obj = _cell_deref(c) - symdict[n] = obj - except NameError: - raise NameError(n) - self.symdict = symdict + super(_AlwaysSeq, self).__init__(func, senslist) + + if self.inouts: + raise AlwaysSeqError(_error.SigAugAssign, v.inouts) + + if self.embedded_func: + raise AlwaysSeqError(_error.EmbeddedFunction) - # now infer outputs to be reset - s = inspect.getsource(func) - s = _dedent(s) - tree = ast.parse(s) - # print ast.dump(tree) - v = _AttrRefTransformer(self) - v.visit(tree) - v = _SigNameVisitor(self.symdict) - v.visit(tree) sigregs = self.sigregs = [] varregs = self.varregs = [] - for n in v.outputs: + for n in self.outputs: reg = self.symdict[n] if isinstance(reg, _Signal): sigregs.append(reg) @@ -144,7 +118,6 @@ class _AlwaysSeq(_Instantiator): for e in reg: sigregs.append(e) - def reset_sigs(self): for s in self.sigregs: s.next = s._init @@ -155,7 +128,7 @@ class _AlwaysSeq(_Instantiator): n, reg, init = v reg._val = init - def genfunc(self): + def genfunc_reset(self): senslist = self.senslist if len(senslist) == 1: senslist = senslist[0] @@ -178,88 +151,3 @@ class _AlwaysSeq(_Instantiator): while 1: yield senslist func() - - -# similar to always_comb, calls for refactoring -# note: make a difference between augmented assign and inout signals - -INPUT, OUTPUT, INOUT = range(3) - -class _SigNameVisitor(ast.NodeVisitor): - def __init__(self, symdict): - self.inputs = set() - self.outputs = set() - self.toplevel = 1 - self.symdict = symdict - self.context = INPUT - - def visit_Module(self, node): - - for n in node.body: - self.visit(n) - - def visit_FunctionDef(self, node): - if self.toplevel: - self.toplevel = 0 # skip embedded functions - for n in node.body: - self.visit(n) - else: - raise AlwaysSeqError(_error.EmbeddedFunction) - - def visit_If(self, node): - if not node.orelse: - if isinstance(node.test, ast.Name) and \ - node.test.id == '__debug__': - return # skip - self.generic_visit(node) - - def visit_Name(self, node): - id = node.id - if id not in self.symdict: - return - s = self.symdict[id] - if isinstance(s, (_Signal, intbv)) or _isListOfSigs(s): - if self.context == INPUT: - self.inputs.add(id) - elif self.context == OUTPUT: - self.outputs.add(id) - elif self.context == INOUT: - raise AlwaysSeqError(_error.SigAugAssign, id) - else: - raise AssertionError("bug in always_seq") - - def visit_Assign(self, node): - self.context = OUTPUT - for n in node.targets: - self.visit(n) - self.context = INPUT - self.visit(node.value) - - def visit_Attribute(self, node): - self.visit(node.value) - - def visit_Subscript(self, node, access=INPUT): - self.visit(node.value) - self.context = INPUT - self.visit(node.slice) - - def visit_AugAssign(self, node, access=INPUT): - self.context = INOUT - self.visit(node.target) - self.context = INPUT - self.visit(node.value) - - def visit_ClassDef(self, node): - pass # skip - - def visit_Exec(self, node): - pass # skip - - def visit_Print(self, node): - pass # skip - - - - - - diff --git a/myhdl/_cell_deref.py b/myhdl/_cell_deref.py deleted file mode 100644 index d9c6d0ed..00000000 --- a/myhdl/_cell_deref.py +++ /dev/null @@ -1,22 +0,0 @@ - -# cell dereferencing hack, thanks to Samuele Pedroni - -from types import FunctionType - -def _proto_acc(v=None): - def acc(): - return v - return acc - -_acc0 = _proto_acc() - -_make_acc = lambda cell: (FunctionType(_acc0.__code__, - _acc0.__globals__, - '#cell_acc', - _acc0.__defaults__, - (cell,) - ) - ) - -def _cell_deref(cell): - return _make_acc(cell)() diff --git a/myhdl/_compat.py b/myhdl/_compat.py index 63818c62..64a38f7e 100644 --- a/myhdl/_compat.py +++ b/myhdl/_compat.py @@ -13,6 +13,7 @@ if not PY2: class_types = (type,) from io import StringIO + from os import set_inheritable import builtins def to_bytes(s): @@ -20,6 +21,7 @@ if not PY2: def to_str(b): return b.decode() + else: string_types = (str, unicode) integer_types = (int, long) @@ -31,3 +33,30 @@ else: to_bytes = _identity to_str = _identity + + def set_inheritable(fd, inheritable): + # This implementation of set_inheritable is based on a code sample in + # [PEP 0446](https://www.python.org/dev/peps/pep-0446/) and on the + # CPython implementation of that proposal which can be browsed [here] + # (hg.python.org/releasing/3.4/file/8671f89107c8/Modules/posixmodule.c#l11130) + if sys.platform == "win32": + import msvcrt + import ctypes.windll.kernel32 as kernel32 + + HANDLE_FLAG_INHERIT = 1 + + if kernel32.SetHandleInformation(msvcrt.get_osfhandle(fd), + HANDLE_FLAG_INHERIT, + 1 if inheritable else 0) == 0: + raise IOError("Failed on HANDLE_FLAG_INHERIT") + else: + import fcntl + + fd_flags = fcntl.fcntl(fd, fcntl.F_GETFD) + + if inheritable: + fd_flags &= ~fcntl.FD_CLOEXEC + else: + fd_flags |= fcntl.FD_CLOEXEC + + fcntl.fcntl(fd, fcntl.F_SETFD, fd_flags) diff --git a/myhdl/_enum.py b/myhdl/_enum.py index 81293f5b..b8f580e0 100644 --- a/myhdl/_enum.py +++ b/myhdl/_enum.py @@ -85,6 +85,9 @@ def enum(*names, **kwargs): __str__ = __repr__ + def __int__(self): + return int(self._val, 2) + def __hex__(self): return hex(int(self._val, 2)) diff --git a/myhdl/_extractHierarchy.py b/myhdl/_extractHierarchy.py index 1ac7cea4..bdc34d9f 100644 --- a/myhdl/_extractHierarchy.py +++ b/myhdl/_extractHierarchy.py @@ -25,7 +25,6 @@ from __future__ import absolute_import import sys import inspect -from inspect import currentframe, getframeinfo, getouterframes import re import string from types import GeneratorType @@ -36,6 +35,7 @@ from myhdl._Signal import _Signal, _isListOfSigs from myhdl._util import _isGenFunc, _flatten, _genfunc from myhdl._misc import _isGenSeq from myhdl._resolverefs import _resolveRefs +from myhdl._getcellvars import _getCellVars _profileFunc = None @@ -48,18 +48,14 @@ _error.InconsistentToplevel = "Inconsistent top level %s for %s - should be 1" class _Instance(object): - __slots__ = ['level', 'obj', 'subs', 'sigdict', 'memdict', 'name', 'func', 'argdict', 'objdict'] - def __init__(self, level, obj, subs, sigdict, memdict, func, argdict, objdict=None): + __slots__ = ['level', 'obj', 'subs', 'sigdict', 'memdict', 'name'] + def __init__(self, level, obj, subs, sigdict, memdict): self.level = level self.obj = obj self.subs = subs self.sigdict = sigdict self.memdict = memdict - self.func = func - self.argdict = argdict - if objdict: - self.objdict = objdict - + self.name = None _memInfoMap = {} @@ -308,17 +304,11 @@ class _HierExtr(object): if isGenSeq and arg: sigdict = {} memdict = {} - argdict = {} - if func: - arglist = inspect.getargspec(func).args - else: - arglist = [] symdict = frame.f_globals.copy() symdict.update(frame.f_locals) cellvars = [] - cellvars.extend(frame.f_code.co_cellvars) - #All nested functions will be in co_consts + # All nested functions will be in co_consts if func: local_gens = [] consts = func.__code__.co_consts @@ -327,6 +317,8 @@ class _HierExtr(object): if genfunc.__code__ in consts: local_gens.append(item) if local_gens: + cellvarlist = _getCellVars(symdict, local_gens) + cellvars.extend(cellvarlist) objlist = _resolveRefs(symdict, local_gens) cellvars.extend(objlist) #for dict in (frame.f_globals, frame.f_locals): @@ -345,9 +337,6 @@ class _HierExtr(object): memdict[n] = m if n in cellvars: m._used = True - # save any other variable in argdict - if (n in arglist) and (n not in sigdict) and (n not in memdict): - argdict[n] = v subs = [] for n, sub in frame.f_locals.items(): @@ -355,8 +344,7 @@ class _HierExtr(object): if elt is sub: subs.append((n, sub)) - - inst = _Instance(self.level, arg, subs, sigdict, memdict, func, argdict) + inst = _Instance(self.level, arg, subs, sigdict, memdict) self.hierarchy.append(inst) self.level -= 1 diff --git a/myhdl/_getcellvars.py b/myhdl/_getcellvars.py new file mode 100644 index 00000000..e9979732 --- /dev/null +++ b/myhdl/_getcellvars.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import +import ast +import itertools +from types import FunctionType + +from myhdl._util import _flatten +from myhdl._enum import EnumType +from myhdl._Signal import SignalType + + +class Data(): + pass + +def _getCellVars(symdict, arg): + gens = _flatten(arg) + data = Data() + data.symdict = symdict + v = _GetCellVars(data) + for gen in gens: + v.visit(gen.ast) + return list(data.objset) + +class _GetCellVars(ast.NodeVisitor): + def __init__(self, data): + self.data = data + self.data.objset = set() + + def visit_Name(self, node): + + if node.id in self.data.symdict: + self.data.objset.add(node.id) + + self.generic_visit(node) diff --git a/myhdl/_instance.py b/myhdl/_instance.py index 956c4e65..44e17879 100644 --- a/myhdl/_instance.py +++ b/myhdl/_instance.py @@ -24,8 +24,10 @@ from __future__ import absolute_import from types import FunctionType from myhdl import InstanceError -from myhdl._util import _isGenFunc +from myhdl._util import _isGenFunc, _makeAST from myhdl._Waiter import _inferWaiter +from myhdl._resolverefs import _AttrRefTransformer +from myhdl._visitors import _SigNameVisitor class _error: pass @@ -33,19 +35,56 @@ _error.NrOfArgs = "decorated generator function should not have arguments" _error.ArgType = "decorated object should be a generator function" -def instance(genFunc): - if not isinstance(genFunc, FunctionType): +def instance(genfunc): + if not isinstance(genfunc, FunctionType): raise InstanceError(_error.ArgType) - if not _isGenFunc(genFunc): + if not _isGenFunc(genfunc): raise InstanceError(_error.ArgType) - if genFunc.__code__.co_argcount > 0: + if genfunc.__code__.co_argcount > 0: raise InstanceError(_error.NrOfArgs) - return _Instantiator(genFunc) + return _Instantiator(genfunc) class _Instantiator(object): - - def __init__(self, genFunc): - self.genfunc = genFunc - self.gen = genFunc() - self.waiter = _inferWaiter(self.gen) - + + def __init__(self, genfunc): + self.genfunc = genfunc + self.gen = genfunc() + # infer symdict + f = self.funcobj + varnames = f.__code__.co_varnames + symdict = {} + for n, v in f.__globals__.items(): + if n not in varnames: + symdict[n] = v + # handle free variables + freevars = f.__code__.co_freevars + if freevars: + closure = (c.cell_contents for c in f.__closure__) + symdict.update(zip(freevars, closure)) + self.symdict = symdict + + tree = self.ast + # print ast.dump(tree) + v = _AttrRefTransformer(self) + v.visit(tree) + v = _SigNameVisitor(self.symdict) + v.visit(tree) + self.inputs = v.inputs + self.outputs = v.outputs + self.inouts = v.inouts + self.embedded_func = v.embedded_func + + @property + def funcobj(self): + return self.genfunc + + @property + def waiter(self): + return self._waiter()(self.gen) + + def _waiter(self): + return _inferWaiter + + @property + def ast(self): + return _makeAST(self.funcobj) diff --git a/myhdl/_intbv.py b/myhdl/_intbv.py index 3b78ae3f..accb7f12 100644 --- a/myhdl/_intbv.py +++ b/myhdl/_intbv.py @@ -1,7 +1,7 @@ # This file is part of the myhdl library, a Python package for using # Python as a Hardware Description Language. # -# Copyright (C) 2003-2013 Jan Decaluwe +# Copyright (C) 2003-2015 Jan Decaluwe # # The myhdl library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License as @@ -262,35 +262,35 @@ class intbv(object): def __rshift__(self, other): if isinstance(other, intbv): - return type(self)(self._val >> other._val) + return intbv(self._val >> other._val) else: - return type(self)(self._val >> other) + return intbv(self._val >> other) def __rrshift__(self, other): return other >> self._val def __and__(self, other): if isinstance(other, intbv): - return type(self)(self._val & other._val) + return intbv(self._val & other._val) else: - return type(self)(self._val & other) + return intbv(self._val & other) def __rand__(self, other): - return type(self)(other & self._val) + return intbv(other & self._val) def __or__(self, other): if isinstance(other, intbv): - return type(self)(self._val | other._val) + return intbv(self._val | other._val) else: - return type(self)(self._val | other) + return intbv(self._val | other) def __ror__(self, other): - return type(self)(other | self._val) + return intbv(other | self._val) def __xor__(self, other): if isinstance(other, intbv): - return type(self)(self._val ^ other._val) + return intbv(self._val ^ other._val) else: - return type(self)(self._val ^ other) + return intbv(self._val ^ other) def __rxor__(self, other): - return type(self)(other ^ self._val) + return intbv(other ^ self._val) def __iadd__(self, other): if isinstance(other, intbv): @@ -401,9 +401,9 @@ class intbv(object): def __invert__(self): if self._nrbits and self._min >= 0: - return type(self)(~self._val & (long(1) << self._nrbits)-1) + return intbv(~self._val & (long(1) << self._nrbits)-1) else: - return type(self)(~self._val) + return intbv(~self._val) def __int__(self): return int(self._val) diff --git a/myhdl/_resolverefs.py b/myhdl/_resolverefs.py index cba6c83e..d77ab06f 100644 --- a/myhdl/_resolverefs.py +++ b/myhdl/_resolverefs.py @@ -3,7 +3,7 @@ import ast import itertools from types import FunctionType -from myhdl._util import _flatten, _makeAST, _genfunc +from myhdl._util import _flatten from myhdl._enum import EnumType from myhdl._Signal import SignalType @@ -18,17 +18,16 @@ def _resolveRefs(symdict, arg): data.symdict = symdict v = _AttrRefTransformer(data) for gen in gens: - func = _genfunc(gen) - tree = _makeAST(func) - v.visit(tree) + v.visit(gen.ast) return data.objlist #TODO: Refactor this into two separate nodetransformers, since _resolveRefs #needs only the names, not the objects -def _suffixer(name): +def _suffixer(name, used_names): suffixed_names = (name+'_renamed{0}'.format(i) for i in itertools.count()) - return itertools.chain([name], suffixed_names) + new_names = itertools.chain([name], suffixed_names) + return next(s for s in new_names if s not in used_names) class _AttrRefTransformer(ast.NodeTransformer): @@ -36,6 +35,7 @@ class _AttrRefTransformer(ast.NodeTransformer): self.data = data self.data.objlist = [] self.myhdl_types = (EnumType, SignalType) + self.name_map = {} def visit_Attribute(self, node): self.generic_visit(node) @@ -44,12 +44,16 @@ class _AttrRefTransformer(ast.NodeTransformer): if node.attr in reserved: return node - #Don't handle subscripts for now. + # Don't handle subscripts for now. if not isinstance(node.value, ast.Name): return node + # Don't handle locals + if node.value.id not in self.data.symdict: + return node + obj = self.data.symdict[node.value.id] - #Don't handle enums and functions, handle signals as long as it is a new attribute + # Don't handle enums and functions, handle signals as long as it is a new attribute if isinstance(obj, (EnumType, FunctionType)): return node elif isinstance(obj, SignalType): @@ -58,8 +62,11 @@ class _AttrRefTransformer(ast.NodeTransformer): attrobj = getattr(obj, node.attr) - name = node.value.id + '_' + node.attr - new_name = next(s for s in _suffixer(name) if s not in self.data.symdict) + orig_name = node.value.id + '.' + node.attr + if orig_name not in self.name_map: + base_name = node.value.id + '_' + node.attr + self.name_map[orig_name] = _suffixer(base_name, self.data.symdict) + new_name = self.name_map[orig_name] self.data.symdict[new_name] = attrobj self.data.objlist.append(new_name) diff --git a/myhdl/_traceSignals.py b/myhdl/_traceSignals.py index 1a693334..abd8dd5a 100644 --- a/myhdl/_traceSignals.py +++ b/myhdl/_traceSignals.py @@ -26,7 +26,6 @@ from __future__ import print_function import sys -from inspect import currentframe, getouterframes import time import os path = os.path @@ -50,12 +49,14 @@ _error.MultipleTraces = "Cannot trace multiple instances simultaneously" class _TraceSignalsClass(object): __slot__ = ("name", + "directory", "timescale", "tracelists" ) def __init__(self): self.name = None + self.directory = None self.timescale = "1ns" self.tracelists = True @@ -82,8 +83,14 @@ class _TraceSignalsClass(object): name = str(self.name) if name is None: raise TraceSignalsError(_error.TopLevelName) + + if self.directory is None: + directory = '' + else: + directory = self.directory + h = _HierExtr(name, dut, *args, **kwargs) - vcdpath = name + ".vcd" + vcdpath = os.path.join(directory, name + ".vcd") if path.exists(vcdpath): backup = vcdpath + '.' + str(path.getmtime(vcdpath)) shutil.copyfile(vcdpath, backup) @@ -111,7 +118,7 @@ def _genNameCode(): while 1: yield _namecode(n) n += 1 - + def _namecode(n): q, r = divmod(n, _mod) code = _codechars[r] @@ -175,10 +182,11 @@ def _writeVcdSigs(f, hierarchy, tracelists): else: print("$var real 1 %s %s $end" % (s._code, n), file=f) # Memory dump by Frederik Teichert, http://teichert-ing.de, date: 2011.03.28 - # The Value Change Dump standard doesn't support multidimensional arrays so + # The Value Change Dump standard doesn't support multidimensional arrays so # all memories are flattened and renamed. if tracelists: for n in memdict.keys(): + print("$scope module {} $end" .format(n), file=f) memindex = 0 for s in memdict[n].mem: sval = _getSval(s) @@ -197,6 +205,7 @@ def _writeVcdSigs(f, hierarchy, tracelists): else: print("$var real 1 %s %s(%i) $end" % (s._code, n, memindex), file=f) memindex += 1 + print("$upscope $end", file=f) for i in range(curlevel): print("$upscope $end", file=f) print(file=f) @@ -205,16 +214,3 @@ def _writeVcdSigs(f, hierarchy, tracelists): for s in siglist: s._printVcd() # initial value print("$end", file=f) - - - - - - - - - - - - - diff --git a/myhdl/_util.py b/myhdl/_util.py index 01b0b792..f3c20e95 100644 --- a/myhdl/_util.py +++ b/myhdl/_util.py @@ -23,15 +23,16 @@ from __future__ import absolute_import from __future__ import print_function - import ast import sys +import os import inspect from tokenize import generate_tokens, untokenize, INDENT from myhdl._compat import integer_types, StringIO + def _printExcInfo(): kind, value = sys.exc_info()[:2] msg = str(kind) diff --git a/myhdl/_visitors.py b/myhdl/_visitors.py new file mode 100644 index 00000000..50c0b709 --- /dev/null +++ b/myhdl/_visitors.py @@ -0,0 +1,89 @@ +import ast + +from myhdl._intbv import intbv +from myhdl._Signal import _Signal, _isListOfSigs + + +class _SigNameVisitor(ast.NodeVisitor): + def __init__(self, symdict): + self.toplevel = 1 + self.symdict = symdict + self.inputs = set() + self.outputs = set() + self.inouts = set() + self.embedded_func = None + self.context = 'input' + + def visit_Module(self, node): + for n in node.body: + self.visit(n) + + def visit_FunctionDef(self, node): + if self.toplevel: + self.toplevel = 0 # skip embedded functions + for n in node.body: + self.visit(n) + else: + self.embedded_func = node.name + + def visit_If(self, node): + if not node.orelse: + if isinstance(node.test, ast.Name) and \ + node.test.id == '__debug__': + return # skip + self.generic_visit(node) + + def visit_Name(self, node): + id = node.id + if id not in self.symdict: + return + s = self.symdict[id] + if isinstance(s, (_Signal, intbv)) or _isListOfSigs(s): + if self.context == 'input': + self.inputs.add(id) + elif self.context == 'output': + self.outputs.add(id) + elif self.context == 'inout': + self.inouts.add(id) + else: + print(self.context) + raise AssertionError("bug in _SigNameVisitor") + + def visit_Assign(self, node): + self.context = 'output' + for n in node.targets: + self.visit(n) + self.context = 'input' + self.visit(node.value) + + def visit_Attribute(self, node): + self.visit(node.value) + + def visit_Call(self, node): + fn = None + if isinstance(node.func, ast.Name): + fn = node.func.id + if fn == "len": + pass + else: + self.generic_visit(node) + + def visit_Subscript(self, node): + self.visit(node.value) + self.context = 'input' + self.visit(node.slice) + + def visit_AugAssign(self, node): + self.context = 'inout' + self.visit(node.target) + self.context = 'input' + self.visit(node.value) + + def visit_ClassDef(self, node): + pass # skip + + def visit_Exec(self, node): + pass # skip + + def visit_Print(self, node): + pass # skip diff --git a/myhdl/conversion/_analyze.py b/myhdl/conversion/_analyze.py index 42502a55..c8d94957 100644 --- a/myhdl/conversion/_analyze.py +++ b/myhdl/conversion/_analyze.py @@ -34,7 +34,6 @@ from collections import defaultdict import myhdl from myhdl import * from myhdl import ConversionError -from myhdl._cell_deref import _cell_deref from myhdl._always_comb import _AlwaysComb from myhdl._always_seq import _AlwaysSeq from myhdl._always import _Always @@ -52,9 +51,6 @@ myhdlObjects = myhdl.__dict__.values() builtinObjects = builtins.__dict__.values() _enumTypeSet = set() -_constDict = {} -_extConstDict = {} - def _makeName(n, prefixes, namedict): # trim empty prefixes @@ -145,14 +141,14 @@ def _analyzeGens(top, absnames): tree = g elif isinstance(g, (_AlwaysComb, _AlwaysSeq, _Always)): f = g.func - tree = _makeAST(f) + tree = g.ast tree.symdict = f.__globals__.copy() tree.callstack = [] # handle free variables tree.nonlocaldict = {} if f.__code__.co_freevars: for n, c in zip(f.__code__.co_freevars, f.__closure__): - obj = _cell_deref(c) + obj = c.cell_contents tree.symdict[n] = obj # currently, only intbv as automatic nonlocals (until Python 3.0) if isinstance(obj, intbv): @@ -171,7 +167,7 @@ def _analyzeGens(top, absnames): v.visit(tree) else: # @instance f = g.gen.gi_frame - tree = _makeAST(f) + tree = g.ast tree.symdict = f.f_globals.copy() tree.symdict.update(f.f_locals) tree.nonlocaldict = {} @@ -248,6 +244,12 @@ class _FirstPassVisitor(ast.NodeVisitor, _ConversionMixin): self.raiseError(node, _error.NotSupported, "list") def visitSliceObj(self, node): self.raiseError(node, _error.NotSupported, "slice object") + + # All try blocks from python 3.3+ + def visit_Try(self, node): + self.raiseError(node, _error.NotSupported, "try statement") + + # Legacy try blocks def visit_TryExcept(self, node): self.raiseError(node, _error.NotSupported, "try-except statement") def visit_TryFinally(self, node): @@ -261,11 +263,19 @@ class _FirstPassVisitor(ast.NodeVisitor, _ConversionMixin): self.visit(node.value) def visit_Call(self, node): - if node.starargs: + # ast.Call signature changed in python 3.5 + # http://greentreesnakes.readthedocs.org/en/latest/nodes.html#Call + if sys.version_info >= (3, 5): + starargs = any(isinstance(arg, ast.Starred) for arg in node.args) + kwargs = any(kw.arg is None for kw in node.keywords) + else: + starargs = node.starargs is not None + kwargs = node.kwargs is not None + + if starargs: self.raiseError(node, _error.NotSupported, "extra positional arguments") - if node.kwargs: + if kwargs: self.raiseError(node, _error.NotSupported, "extra named arguments") - # f = eval(_unparse(node.node), self.tree.symdict) self.generic_visit(node) def visit_Compare(self, node): @@ -517,12 +527,6 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): if isinstance(obj, modbv): if not obj._hasFullRange(): self.raiseError(node, _error.ModbvRange, n) - ws = getattr(obj, 'lenStr', False) - ext = getattr(obj, 'external', False) - if ws and ws in self.tree.symdict: - _constDict[ws] = self.tree.symdict[ws] - if ext: - _extConstDict[ws] = self.tree.symdict[ws] if n in self.tree.vardict: curObj = self.tree.vardict[n] if isinstance(obj, type(curObj)): @@ -601,7 +605,7 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): # handle free variables if f.__code__.co_freevars: for n, c in zip(f.__code__.co_freevars, f.__closure__): - obj = _cell_deref(c) + obj = c.cell_contents if not isinstance(obj, (integer_types, _Signal)): self.raiseError(node, _error.FreeVarTypeError, n) tree.symdict[n] = obj @@ -817,12 +821,6 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): else: if sig._type is bool: node.edge = sig.posedge - ws = getattr(sig._val, 'lenStr', False) - ext = getattr(sig._val, 'external', False) - if ws and ws in self.tree.symdict: - _constDict[ws] = self.tree.symdict[ws] - if ext: - _extConstDict[ws] = self.tree.symdict[ws] if self.access == _access.INPUT: self.tree.inputs.add(n) elif self.access == _access.OUTPUT: @@ -859,27 +857,11 @@ class _AnalyzeVisitor(ast.NodeVisitor, _ConversionMixin): else: assert False, "unexpected mem access %s %s" % (n, self.access) self.tree.hasLos = True - ws = getattr(m.elObj._val, 'lenStr', False) - ext = getattr(m.elObj._val, 'external', False) - if ws and ws in self.tree.symdict: - _constDict[ws] = self.tree.symdict[ws] - if ext: - _extConstDict[ws] = self.tree.symdict[ws] elif isinstance(node.obj, int): node.value = node.obj - # put VHDL compliant integer constants in global dict - if n not in _constDict and abs(node.obj) < 2**31: - _constDict[n] = node.obj if n in self.tree.nonlocaldict: # hack: put nonlocal intbv's in the vardict self.tree.vardict[n] = v = node.obj - # typedef string for nonlocal intbv's - ws = getattr(v, 'lenStr', False) - ext = getattr(v, 'external', False) - if ws and ws in self.tree.symdict: - _constDict[ws] = self.tree.symdict[ws] - if ext: - _extConstDict[ws] = self.tree.symdict[ws] elif n in builtins.__dict__: node.obj = builtins.__dict__[n] else: @@ -1240,7 +1222,7 @@ def isboundmethod(m): return ismethod(m) and m.__self__ is not None -def _analyzeTopFunc(top_inst, func, *args, **kwargs): +def _analyzeTopFunc(func, *args, **kwargs): tree = _makeAST(func) v = _AnalyzeTopFuncVisitor(func, tree, *args, **kwargs) v.visit(tree) diff --git a/myhdl/conversion/_toVHDL.py b/myhdl/conversion/_toVHDL.py index 5cd444da..96ff5135 100644 --- a/myhdl/conversion/_toVHDL.py +++ b/myhdl/conversion/_toVHDL.py @@ -48,11 +48,12 @@ from myhdl._instance import _Instantiator from myhdl.conversion._misc import (_error,_kind,_context, _ConversionMixin, _Label, _genUniqueSuffix, _isConstant) from myhdl.conversion._analyze import (_analyzeSigs, _analyzeGens, _analyzeTopFunc, - _Ram, _Rom, _enumTypeSet, _constDict, _extConstDict) + _Ram, _Rom, _enumTypeSet) from myhdl._Signal import _Signal,_WaiterList from myhdl.conversion._toVHDLPackage import _package from myhdl._util import _flatten from myhdl._compat import integer_types, class_types, StringIO +from myhdl._ShadowSignal import _TristateSignal, _TristateDriver _version = myhdl.__version__.replace('.','') @@ -162,8 +163,6 @@ class _ToVHDLConvertor(object): _genUniqueSuffix.reset() _enumTypeSet.clear() _enumPortTypeSet.clear() - _constDict.clear() - _extConstDict.clear() arglist = _flatten(h.top) _checkArgs(arglist) @@ -173,8 +172,7 @@ class _ToVHDLConvertor(object): _annotateTypes(genlist) ### infer interface - top_inst = h.hierarchy[0] - intf = _analyzeTopFunc(top_inst, func, *args, **kwargs) + intf = _analyzeTopFunc(func, *args, **kwargs) intf.name = name # sanity checks on interface for portname in intf.argnames: @@ -211,7 +209,6 @@ class _ToVHDLConvertor(object): _writeCustomPackage(vfile, intf) _writeModuleHeader(vfile, intf, needPck, lib, arch, useClauses, doc, stdLogicPorts) _writeFuncDecls(vfile) - _writeConstants(vfile) _writeTypeDefs(vfile) _writeSigDecls(vfile, intf, siglist, memlist) _writeCompDecls(vfile, compDecls) @@ -327,10 +324,11 @@ def _writeModuleHeader(f, intf, needPck, lib, arch, useClauses, doc, stdLogicPor if convertPort: pt = "std_logic_vector" if s._driven: - if s._read: - warnings.warn("%s: %s" % (_error.OutputPortRead, portname), - category=ToVHDLWarning - ) + if s._read : + if not isinstance(s, _TristateSignal): + warnings.warn("%s: %s" % (_error.OutputPortRead, portname), + category=ToVHDLWarning + ) f.write("\n %s: inout %s%s" % (portname, pt, r)) else: f.write("\n %s: out %s%s" % (portname, pt, r)) @@ -358,28 +356,6 @@ def _writeFuncDecls(f): return # print >> f, package -def _writeConstants(f): - f.write("\n") - # guess nice representation - for c in _constDict: - if c in _extConstDict: - continue - v = _constDict[c] - s = str(int(v)) - sign = '' - if v < 0: - sign = '-' - for i in range(4, 31): - if abs(v) == 2**i: - s = "%s2**%s" % (sign, i) - break - if abs(v) == 2**i-1: - s = "%s2**%s-1" % (sign, i) - break - v = _constDict[c] - f.write("constant %s: integer := %s;\n" % (c, s)) - f.write("\n") - def _writeTypeDefs(f): f.write("\n") sortedList = list(_enumTypeSet) @@ -400,7 +376,7 @@ def _writeSigDecls(f, intf, siglist, memlist): r = _getRangeString(s) p = _getTypeString(s) if s._driven: - if not s._read: + if not s._read and not isinstance(s, _TristateDriver): warnings.warn("%s: %s" % (_error.UnreadSignal, s._name), category=ToVHDLWarning ) @@ -461,11 +437,7 @@ def _getRangeString(s): elif s._type is bool: return '' elif s._nrbits is not None: - ls = getattr(s, 'lenStr', False) - if ls: - msb = ls + '-1' - else: - msb = s._nrbits-1 + msb = s._nrbits-1 return "(%s downto 0)" % msb else: raise AssertionError @@ -516,9 +488,17 @@ def _convertGens(genlist, siglist, memlist, vfile): w = len(s) assert w != 0 if s._min < 0: - pre, suf = "to_signed(", ", %s)" % w + if w <= 31: + pre, suf = "to_signed(", ", %s)" % w + else: + pre, suf = "signed'(", ")" + c = '"%s"' % bin(c, w) else: - pre, suf = "to_unsigned(", ", %s)" % w + if w <= 31: + pre, suf = "to_unsigned(", ", %s)" % w + else: + pre, suf = "unsigned'(", ")" + c = '"%s"' % bin(c, w) else: raise ToVHDLError("Unexpected type for constant signal", s._name) print("%s <= %s%s%s;" % (s._name, pre, c, suf), file=vfile) @@ -791,6 +771,14 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.write(")") def visit_UnaryOp(self, node): + # in python3 a negative Num is represented as an USub of a positive Num + # Fix: restore python2 behavior by a shortcut: invert value of Num, inherit + # vhdl type from UnaryOp node, and visit the modified operand + if isinstance(node.op, ast.USub) and isinstance(node.operand, ast.Num): + node.operand.n = -node.operand.n + node.operand.vhd = node.vhd + self.visit(node.operand) + return pre, suf = self.inferCast(node.vhd, node.vhdOri) self.write(pre) self.write("(") @@ -988,6 +976,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): node.args[0].s = ord(node.args[0].s) elif f in integer_types: opening, closing = '', '' + pre, suf = self.inferCast(node.vhd, node.vhdOri) # convert number argument to integer if isinstance(node.args[0], ast.Num): node.args[0].n = int(node.args[0].n) @@ -1022,7 +1011,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.write(f.__name__) elif f is delay: self.visit(node.args[0]) - self.write(" ns") + self.write(" * 1 ns") return elif f is concat: pre, suf = self.inferCast(node.vhd, node.vhdOri) @@ -1119,17 +1108,16 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): self.write(';') def visit_IfExp(self, node): - pre, suf = self.inferCast(node.vhd, node.body.vhdOri) - self.write(pre) - self.visit(node.body) - self.write(suf) - self.write(' when ') + # propagate the node's vhd attribute + node.body.vhd = node.orelse.vhd = node.vhd + self.write('tern_op(') + self.write('cond => ') self.visit(node.test) - self.write(' else ') - pre, suf = self.inferCast(node.vhd, node.orelse.vhdOri) - self.write(pre) + self.write(', if_true => ') + self.visit(node.body) + self.write(', if_false => ') self.visit(node.orelse) - self.write(suf) + self.write(')') def visit_For(self, node): self.labelStack.append(node.breakLabel) @@ -1305,10 +1293,11 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): else: s = "True" elif n == 'None': - if node.vhd.size == 1: + if isinstance(node.vhd, vhd_std_logic): s = "'Z'" else: - s = "(others => 'Z')" + assert hasattr(node.vhd, 'size') + s = '"%s"' % ('Z' * node.vhd.size) elif n in self.tree.vardict: s = n obj = self.tree.vardict[n] @@ -1328,40 +1317,27 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): obj = self.tree.symdict[n] s = n if isinstance(obj, bool): - s = "'%s'" % int(obj) - # print the symbol for a boolean in the global constant dict - if n in _constDict and obj == _constDict[n]: - if isinstance(node.vhd, vhd_boolean): - s = "bool(%s)" % n - elif isinstance(obj, integer_types): - # print the symbol for an integer in the global constant dict - if n in _constDict and obj == _constDict[n]: - assert abs(obj) < 2**31 - if isinstance(node.vhd, vhd_int): - s = n - elif isinstance(node.vhd, vhd_boolean): - s = "bool(%s)" % n - elif isinstance(node.vhd, vhd_std_logic): - s = "stdl(%s)" % n - elif isinstance(node.vhd, vhd_unsigned): - s = "to_unsigned(%s, %s)" % (n, node.vhd.size) - elif isinstance(node.vhd, vhd_signed): - s = "to_signed(%s, %s)" % (n, node.vhd.size) + if isinstance(node.vhd, vhd_std_logic): + s = "'%s'" % int(obj) else: - if isinstance(node.vhd, vhd_int): - s = self.IntRepr(obj) - elif isinstance(node.vhd, vhd_std_logic): - s = "'%s'" % int(obj) - elif isinstance(node.vhd, vhd_unsigned): - if abs(obj) < 2** 31: - s = "to_unsigned(%s, %s)" % (n, node.vhd.size) - else: - s = 'unsigned\'("%s")' % bin(obj, node.vhd.size) - elif isinstance(node.vhd, vhd_signed): - if abs(obj) < 2** 31: - s = "to_signed(%s, %s)" % (n, node.vhd.size) - else: - s = 'signed\'("%s")' % bin(obj, node.vhd.size) + s = "%s" % obj + elif isinstance(obj, integer_types): + if isinstance(node.vhd, vhd_int): + s = self.IntRepr(obj) + elif isinstance(node.vhd, vhd_boolean): + s = "%s" % bool(obj) + elif isinstance(node.vhd, vhd_std_logic): + s = "'%s'" % int(obj) + elif isinstance(node.vhd, vhd_unsigned): + if abs(obj) < 2** 31: + s = "to_unsigned(%s, %s)" % (obj, node.vhd.size) + else: + s = 'unsigned\'("%s")' % bin(obj, node.vhd.size) + elif isinstance(node.vhd, vhd_signed): + if abs(obj) < 2** 31: + s = "to_signed(%s, %s)" % (obj, node.vhd.size) + else: + s = 'signed\'("%s")' % bin(obj, node.vhd.size) elif isinstance(obj, _Signal): s = str(obj) ori = inferVhdlObj(obj) @@ -1948,29 +1924,20 @@ class vhd_boolean(vhd_type): return 'boolean' class vhd_vector(vhd_type): - def __init__(self, size=0, lenStr=False): + def __init__(self, size=0): vhd_type.__init__(self, size) - self.lenStr = lenStr class vhd_unsigned(vhd_vector): def toStr(self, constr=True): if constr: - ls = self.lenStr - if ls: - return "unsigned(%s-1 downto 0)" % ls - else: - return "unsigned(%s downto 0)" % (self.size-1) + return "unsigned(%s downto 0)" % (self.size-1) else: return "unsigned" class vhd_signed(vhd_vector): def toStr(self, constr=True): if constr: - ls = self.lenStr - if ls: - return "signed(%s-1 downto 0)" % ls - else: - return "signed(%s downto 0)" % (self.size-1) + return "signed(%s downto 0)" % (self.size-1) else: return "signed" @@ -2008,11 +1975,10 @@ def inferVhdlObj(obj): vhd = None if (isinstance(obj, _Signal) and obj._type is intbv) or \ isinstance(obj, intbv): - ls = getattr(obj, 'lenStr', False) if obj.min is None or obj.min < 0: - vhd = vhd_signed(size=len(obj), lenStr=ls) + vhd = vhd_signed(size=len(obj)) else: - vhd = vhd_unsigned(size=len(obj), lenStr=ls) + vhd = vhd_unsigned(size=len(obj)) elif (isinstance(obj, _Signal) and obj._type is bool) or \ isinstance(obj, bool): vhd = vhd_std_logic() @@ -2306,10 +2272,3 @@ def _annotateTypes(genlist): continue v = _AnnotateTypesVisitor(tree) v.visit(tree) - - - - - - - diff --git a/myhdl/conversion/_toVHDLPackage.py b/myhdl/conversion/_toVHDLPackage.py index 86f7d46e..f81b6556 100644 --- a/myhdl/conversion/_toVHDLPackage.py +++ b/myhdl/conversion/_toVHDLPackage.py @@ -58,6 +58,12 @@ package pck_myhdl_%(version)s is function "-" (arg: unsigned) return signed; + function tern_op(cond: boolean; if_true: std_logic; if_false: std_logic) return std_logic; + + function tern_op(cond: boolean; if_true: unsigned; if_false: unsigned) return unsigned; + + function tern_op(cond: boolean; if_true: signed; if_false: signed) return signed; + end pck_myhdl_%(version)s; @@ -157,6 +163,33 @@ package body pck_myhdl_%(version)s is return - signed(resize(arg, arg'length+1)); end function "-"; + function tern_op(cond: boolean; if_true: std_logic; if_false: std_logic) return std_logic is + begin + if cond then + return if_true; + else + return if_false; + end if; + end function tern_op; + + function tern_op(cond: boolean; if_true: unsigned; if_false: unsigned) return unsigned is + begin + if cond then + return if_true; + else + return if_false; + end if; + end function tern_op; + + function tern_op(cond: boolean; if_true: signed; if_false: signed) return signed is + begin + if cond then + return if_true; + else + return if_false; + end if; + end function tern_op; + end pck_myhdl_%(version)s; """ % {'version' : _shortversion} diff --git a/myhdl/conversion/_toVerilog.py b/myhdl/conversion/_toVerilog.py index 5626a8d7..987536a6 100644 --- a/myhdl/conversion/_toVerilog.py +++ b/myhdl/conversion/_toVerilog.py @@ -50,7 +50,7 @@ from myhdl.conversion._misc import (_error, _kind, _context, from myhdl.conversion._analyze import (_analyzeSigs, _analyzeGens, _analyzeTopFunc, _Ram, _Rom) from myhdl._Signal import _Signal - +from myhdl._ShadowSignal import _TristateSignal, _TristateDriver _converting = 0 _profileFunc = None @@ -153,8 +153,8 @@ class _ToVerilogConvertor(object): genlist = _analyzeGens(arglist, h.absnames) siglist, memlist = _analyzeSigs(h.hierarchy) _annotateTypes(genlist) - top_inst = h.hierarchy[0] - intf = _analyzeTopFunc(top_inst, func, *args, **kwargs) + + intf = _analyzeTopFunc(func, *args, **kwargs) intf.name = name doc = _makeDoc(inspect.getdoc(func)) @@ -255,11 +255,15 @@ def _writeModuleHeader(f, intf, doc): r = _getRangeString(s) p = _getSignString(s) if s._driven: - if s._read: - warnings.warn("%s: %s" % (_error.OutputPortRead, portname), - category=ToVerilogWarning - ) - print("output %s%s%s;" % (p, r, portname), file=f) + if s._read : + if not isinstance(s, _TristateSignal): + warnings.warn("%s: %s" % (_error.OutputPortRead, portname), + category=ToVerilogWarning + ) + if isinstance(s, _TristateSignal): + print("inout %s%s%s;" % (p, r, portname), file=f) + else: + print("output %s%s%s;" % (p, r, portname), file=f) if s._driven == 'reg': print("reg %s%s%s;" % (p, r, portname), file=f) else: @@ -283,7 +287,7 @@ def _writeSigDecls(f, intf, siglist, memlist): r = _getRangeString(s) p = _getSignString(s) if s._driven: - if not s._read: + if not s._read and not isinstance(s, _TristateDriver): warnings.warn("%s: %s" % (_error.UnreadSignal, s._name), category=ToVerilogWarning ) @@ -331,7 +335,9 @@ def _writeSigDecls(f, intf, siglist, memlist): c = int(s.val) else: raise ToVerilogError("Unexpected type for constant signal", s._name) - print("assign %s = %s;" % (s._name, c), file=f) + c_len = s._nrbits + c_str = "%s"%c + print("assign %s = %s'd%s;" % (s._name, c_len, c_str), file=f) print(file=f) # shadow signal assignments for s in siglist: @@ -1032,7 +1038,7 @@ class _ConvertVisitor(ast.NodeVisitor, _ConversionMixin): elif n in self.tree.symdict: obj = self.tree.symdict[n] if isinstance(obj, bool): - s = "%s" % int(obj) + s = "1'b%s" % int(obj) elif isinstance(obj, integer_types): s = self.IntRepr(obj) elif isinstance(obj, _Signal): @@ -1573,6 +1579,3 @@ def _annotateTypes(genlist): continue v = _AnnotateTypesVisitor(tree) v.visit(tree) - - - diff --git a/myhdl/conversion/_verify.py b/myhdl/conversion/_verify.py index cdafcfb3..34e4192f 100644 --- a/myhdl/conversion/_verify.py +++ b/myhdl/conversion/_verify.py @@ -6,6 +6,8 @@ import tempfile import subprocess import difflib +from collections import namedtuple + import myhdl from myhdl._Simulation import Simulation from myhdl.conversion._toVHDL import toVHDL @@ -15,14 +17,10 @@ _version = myhdl.__version__.replace('.','') # strip 'dev' for version _version = _version.replace('dev','') -_simulators = [] -_hdlMap = {} -_analyzeCommands = {} -_elaborateCommands = {} -_simulateCommands = {} -_skiplinesMap = {} -_skipcharsMap = {} -_ignoreMap = {} +_simulators = {} + +sim = namedtuple('sim', 'name hdl analyze elaborate simulate skiplines skipchars ignore') + def registerSimulator(name=None, hdl=None, analyze=None, elaborate=None, simulate=None, skiplines=None, skipchars=None, ignore=None): @@ -38,23 +36,23 @@ def registerSimulator(name=None, hdl=None, analyze=None, elaborate=None, simulat raise ValueError("Invalid elaborate command") if not isinstance(simulate, str) or (simulate.strip() == ""): raise ValueError("Invalid simulator command") - _simulators.append(name) - _hdlMap[name] = hdl - _analyzeCommands[name] = analyze - _elaborateCommands[name] = elaborate - _simulateCommands[name] = simulate - _skiplinesMap[name] = skiplines - _skipcharsMap[name] = skipchars - _ignoreMap[name] = ignore + _simulators[name] = sim(name, hdl, analyze, elaborate, simulate, skiplines, skipchars, ignore) registerSimulator( - name="GHDL", + name="ghdl", hdl="VHDL", analyze="ghdl -a --workdir=work pck_myhdl_%(version)s.vhd %(topname)s.vhd", elaborate="ghdl -e --workdir=work -o %(unitname)s %(topname)s", simulate="ghdl -r --workdir=work %(unitname)s" ) +registerSimulator( + name="nvc", + hdl="VHDL", + analyze="nvc --work=work_nvc -a pck_myhdl_%(version)s.vhd %(topname)s.vhd", + elaborate="nvc --work=work_nvc -e %(topname)s", + simulate="nvc --work=work_nvc -r %(topname)s" + ) registerSimulator( name="vlog", @@ -63,7 +61,7 @@ registerSimulator( simulate='vsim work_vlog.%(topname)s -quiet -c -do "run -all; quit -f"', skiplines=6, skipchars=2, - ignore=("# **", "# run -all") + ignore=("# **", "# //", "# run -all") ) registerSimulator( @@ -73,12 +71,12 @@ registerSimulator( simulate='vsim work_vcom.%(topname)s -quiet -c -do "run -all; quit -f"', skiplines=6, skipchars=2, - ignore=("# **", "# Time:", "# run -all") + ignore=("# **", "# //", "# Time:", "# run -all") ) registerSimulator( - name="icarus", + name="iverilog", hdl="Verilog", analyze="iverilog -o %(topname)s.o %(topname)s.v", simulate="vvp %(topname)s.o" @@ -98,31 +96,38 @@ class _VerificationClass(object): __slots__ = ("simulator", "_analyzeOnly") def __init__(self, analyzeOnly=False): - self.simulator = "GHDL" + self.simulator = None self._analyzeOnly = analyzeOnly def __call__(self, func, *args, **kwargs): + if not self.simulator: + raise ValueError("No simulator specified") + if self.simulator not in _simulators: + raise ValueError("Simulator %s is not registered" % self.simulator) + hdlsim = _simulators[self.simulator] + hdl = hdlsim.hdl + if hdl == 'Verilog' and toVerilog.name is not None: + name = toVerilog.name + elif hdl == 'VHDL' and toVHDL.name is not None: + name = toVHDL.name + else: + name = func.__name__ + vals = {} - vals['topname'] = func.__name__ - vals['unitname'] = func.__name__.lower() + vals['topname'] = name + vals['unitname'] = name.lower() vals['version'] = _version - hdlsim = self.simulator - if not hdlsim: - raise ValueError("No simulator specified") - if not hdlsim in _simulators: - raise ValueError("Simulator %s is not registered" % hdlsim) - hdl = _hdlMap[hdlsim] - analyze = _analyzeCommands[hdlsim] % vals - elaborate = _elaborateCommands[hdlsim] + analyze = hdlsim.analyze % vals + elaborate = hdlsim.elaborate if elaborate is not None: elaborate = elaborate % vals - simulate = _simulateCommands[hdlsim] % vals - skiplines = _skiplinesMap[hdlsim] - skipchars = _skipcharsMap[hdlsim] - ignore = _ignoreMap[hdlsim] + simulate = hdlsim.simulate % vals + skiplines = hdlsim.skiplines + skipchars = hdlsim.skipchars + ignore = hdlsim.ignore if hdl == "VHDL": inst = toVHDL(func, *args, **kwargs) @@ -132,7 +137,7 @@ class _VerificationClass(object): if hdl == "VHDL": if not os.path.exists("work"): os.mkdir("work") - if hdlsim in ('vlog', 'vcom'): + if hdlsim.name in ('vlog', 'vcom'): if not os.path.exists("work_vsim"): try: subprocess.call("vlib work_vlog", shell=True) @@ -194,10 +199,10 @@ class _VerificationClass(object): glines = [line[skipchars:] for line in glines] flinesNorm = [line.lower() for line in flines] glinesNorm = [line.lower() for line in glines] - g = difflib.unified_diff(flinesNorm, glinesNorm, fromfile=hdlsim, tofile=hdl) + g = difflib.unified_diff(flinesNorm, glinesNorm, fromfile=hdlsim.name, tofile=hdl) MyHDLLog = "MyHDL.log" - HDLLog = hdlsim + ".log" + HDLLog = hdlsim.name + ".log" try: os.remove(MyHDLLog) os.remove(HDLLog) diff --git a/myhdl/test/__init__.py b/myhdl/test/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/myhdl/test/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/myhdl/test/bugs/GHDL.py b/myhdl/test/bugs/GHDL.py deleted file mode 100644 index 655506a2..00000000 --- a/myhdl/test/bugs/GHDL.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify -from myhdl.conversion import analyze - -verify.simulator = "GHDL" -analyze.simulator = "GHDL" diff --git a/myhdl/test/bugs/Makefile b/myhdl/test/bugs/Makefile index 8f794012..89ea1cd5 100644 --- a/myhdl/test/bugs/Makefile +++ b/myhdl/test/bugs/Makefile @@ -1,21 +1,24 @@ -all: vlog vcom +all: ghdl iverilog vlog: - py.test vlog.py test_*.py + py.test --sim vlog vcom: - py.test vcom.py test_*.py + py.test --sim vcom -GHDL: - py.test GHDL.py test_*.py +ghdl: + py.test --sim ghdl -icarus: - py.test icarus.py test_*.py +iverilog: + py.test --sim iverilog cver: - py.test cver.py test_*.py + py.test --sim cver clean: - rm *.o *.out *.v *.vhd *.pyc *~ *.vcd* *.log *_ghdl + +gitclean: + git clean -dfx diff --git a/myhdl/test/bugs/__init__.py b/myhdl/test/bugs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/bugs/cver.py b/myhdl/test/bugs/cver.py deleted file mode 100644 index d9180915..00000000 --- a/myhdl/test/bugs/cver.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify - -verify.simulator = "cver" diff --git a/myhdl/test/bugs/icarus.py b/myhdl/test/bugs/icarus.py deleted file mode 100644 index 3bf9d7d4..00000000 --- a/myhdl/test/bugs/icarus.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify -from myhdl.conversion import analyze - -verify.simulator = "icarus" -analyze.simulator = "icarus" diff --git a/myhdl/test/bugs/test_issue134.py b/myhdl/test/bugs/test_issue134.py new file mode 100644 index 00000000..7c9022a8 --- /dev/null +++ b/myhdl/test/bugs/test_issue134.py @@ -0,0 +1,31 @@ +""" +When an interface signal gets passed into a function, it +can get renamed to the name of the argument. When the +function is called multiple times, this causes name collisions """ + +from __future__ import absolute_import +import pytest +from myhdl import * +from myhdl.conversion import analyze + +class AB: + def __init__(self): + self.a = Signal(bool(False)) + self.b = Signal(bool(False)) + +def invert(sigin, sigout): + @always_comb + def foo(): + sigout.next = not sigin + return foo + +def issue_134(ab_in, ab_out): + """ Instantiate an inverter for each signal """ + inverta = invert(ab_in.a, ab_out.a) + invertb = invert(ab_in.b, ab_out.b) + return inverta, invertb + +@pytest.mark.xfail +def test_issue_134(): + """ check for port name collision""" + assert analyze(issue_134, AB(), AB()) == 0 diff --git a/myhdl/test/bugs/test_issue_117.py b/myhdl/test/bugs/test_issue_117.py new file mode 100644 index 00000000..ac61ed75 --- /dev/null +++ b/myhdl/test/bugs/test_issue_117.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import +from myhdl import * +from myhdl.conversion import analyze + +def issue_117(clk, sdi, pdo, sel, const=False): + assert isinstance(const, (bool, intbv)) + delay_reg = Signal(intbv(0)[8:]) + rlen = len(pdo) + plen = 1 if isinstance(const, bool) else len(const) + @always(clk.posedge) + def rtl(): + if sel == 0: + delay_reg.next = concat(const, delay_reg[rlen-plen-1:1], sdi) + elif sel == 1: + delay_reg.next = concat(delay_reg[rlen-1:plen+1], const, sdi) + elif sel == 2: + delay_reg.next = concat(delay_reg[rlen-1:plen+1], sdi, const) + pdo.next = delay_reg + return rtl + +def test_issue_117_1(): + clk, sdi = [Signal(bool(0)) for _ in range(2)] + pdo = Signal(intbv(0)[8:]) + sel = Signal(intbv(0, min=0, max=3)) + toVHDL.name = toVerilog.name = 'issue_117_1' + assert analyze(issue_117, clk, sdi, pdo, sel, const=bool(0))== 0 + + +def test_issue_117_2(): + clk, sdi = [Signal(bool(0)) for _ in range(2)] + pdo = Signal(intbv(0)[8:]) + sel = Signal(intbv(0, min=0, max=3)) + toVHDL.name = toVerilog.name = 'issue_117_2' + assert analyze(issue_117, clk, sdi, pdo, sel, const=False)== 0 + + +def test_issue_117_3(): + clk, sdi = [Signal(bool(0)) for _ in range(2)] + pdo = Signal(intbv(0)[8:]) + sel = Signal(intbv(0, min=0, max=3)) + toVHDL.name = toVerilog.name = 'issue_117_3' + assert analyze(issue_117, clk, sdi, pdo, sel, const=intbv(0)[1:])== 0 + + +if __name__ == '__main__': + analyze.simulator='vlog' + test_issue_117_1() + + diff --git a/myhdl/test/bugs/test_issue_122.py b/myhdl/test/bugs/test_issue_122.py new file mode 100644 index 00000000..f7404778 --- /dev/null +++ b/myhdl/test/bugs/test_issue_122.py @@ -0,0 +1,30 @@ +from __future__ import absolute_import +from myhdl import * +from myhdl.conversion import verify + +def issue_122(dout, i): + + d = i*10+1 + + @instance + def write(): + # dout[i].next = int(i) + dout[i].next = i + yield delay(d) + print(int(dout[i])) + + if i == 0: + return write + else: + inst = issue_122(dout, i-1) + return write, inst + +def tb_issue_122(): + n = 7 + dout = [Signal(intbv(0, min=0, max=n+1)) for i in range(n+1)] + inst = issue_122(dout, n) + return inst + +def test_issue_122(): + assert verify(tb_issue_122) == 0 + diff --git a/myhdl/test/bugs/test_issue_127.py b/myhdl/test/bugs/test_issue_127.py new file mode 100644 index 00000000..f8f4a5ed --- /dev/null +++ b/myhdl/test/bugs/test_issue_127.py @@ -0,0 +1,148 @@ +''' Bitonic sort ''' + +# http://www.myhdl.org/examples/bitonic/ + +from __future__ import absolute_import + +import unittest +from random import randrange + +from myhdl import Signal, intbv, \ + always_comb, instance, \ + delay, toVHDL, StopSimulation + + +ASCENDING = True +DESCENDING = False + + +# modules + +def compare(a_1, a_2, z_1, z_2, direction): + """ Combinatorial circuit with two input and two output signals. + Sorting to 'direction'. """ + + @always_comb + def logic(): + ''' Combinatorial logic ''' + if direction == (a_1 > a_2): + z_1.next = a_2 + z_2.next = a_1 + else: + z_1.next = a_1 + z_2.next = a_2 + + return logic + + +def feedthru(in_a, out_z): + """ Equivalent of 'doing nothing'. """ + + @always_comb + def logic(): + ''' Combinatorial logic ''' + out_z.next = in_a + + return logic + + +def bitonic_merge(list_a, list_z, direction): + """ bitonicMerge: + Generates the output from the input list of signals. + Recursive. """ + len_list = len(list_a) + half_len = len_list//2 + width = len(list_a[0]) + + if len_list > 1: + tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)] + + comp = [compare(list_a[i], list_a[i+half_len], tmp[i], tmp[i+half_len], \ + direction) for i in range(half_len)] + + lo_merge = bitonic_merge( tmp[:half_len], list_z[:half_len], direction ) + hi_merge = bitonic_merge( tmp[half_len:], list_z[half_len:], direction ) + + return comp, lo_merge, hi_merge + else: + feed = feedthru(list_a[0], list_z[0]) + return feed + + +def bitonic_sort(list_a, list_z, direction): + """ bitonicSort: + Produces a bitonic sequence. + Recursive. """ + len_list = len(list_a) + half_len = len_list//2 + width = len(list_a[0]) + + if len_list > 1: + tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)] + + lo_sort = bitonic_sort( list_a[:half_len], tmp[:half_len], ASCENDING ) + hi_sort = bitonic_sort( list_a[half_len:], tmp[half_len:], DESCENDING ) + + merge = bitonic_merge( tmp, list_z, direction ) + return lo_sort, hi_sort, merge + else: + feed = feedthru(list_a[0], list_z[0]) + return feed + + +# tests + +def array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7, + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7): + ''' Sort Array with 8 values ''' + + list_a = [a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7] + list_z = [z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7] + + sort = bitonic_sort(list_a, list_z, ASCENDING) + return sort + + +class TestBitonicSort(unittest.TestCase): + ''' Test class for bitonic sort ''' + + def test_sort(self): + """ Check the functionality of the bitonic sort """ + length = 8 + width = 4 + + def test_impl(): + ''' test implementation ''' + inputs = [ Signal(intbv(0)[width:]) for _ in range(length) ] + outputs = [ Signal(intbv(0)[width:]) for _ in range(length) ] + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7 = outputs + a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7 = inputs + + inst = array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7, + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7) + + @instance + def check(): + ''' testbench input and validation ''' + for i in range(100): + data = [randrange(2**width) for i in range(length)] + for i in range(length): + inputs[i].next = data[i] + yield delay(10) + data.sort() + self.assertEqual(data, outputs, 'wrong data') + raise StopSimulation + + return inst, check + + + +# convert + +def test_issue_127(): + ''' Convert to VHDL ''' + length = 8 + width = 4 + sigs = [Signal(intbv(0)[width:]) for _ in range(2*length)] + toVHDL(array8sorter, *sigs) + diff --git a/myhdl/test/bugs/test_issue_133.py b/myhdl/test/bugs/test_issue_133.py new file mode 100644 index 00000000..09dd1e59 --- /dev/null +++ b/myhdl/test/bugs/test_issue_133.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import +from myhdl import * +from myhdl.conversion import verify + +def issue_133(): + z = Signal(False) + large_signal = Signal(intbv(123456789123456, min=0, max=2**256)) + @instance + def check(): + z.next = large_signal[10] + yield delay(10) + print (large_signal[31:]) + print (large_signal[62:31]) + print (large_signal[93:62]) + + return check + +def test_issue_133(): + assert verify(issue_133) == 0 diff --git a/myhdl/test/bugs/test_issue_18.py b/myhdl/test/bugs/test_issue_18.py index 92157f1b..be6ee32a 100644 --- a/myhdl/test/bugs/test_issue_18.py +++ b/myhdl/test/bugs/test_issue_18.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from myhdl import * from myhdl.conversion import analyze @@ -27,5 +28,5 @@ clk = Signal(bool(0)) def test_issue_18(): toVHDL.std_logic_ports = True - analyze(issue_18, dout, din, addr, we, clk) == 0 + assert analyze(issue_18, dout, din, addr, we, clk) == 0 diff --git a/myhdl/test/bugs/test_issue_98.py b/myhdl/test/bugs/test_issue_98.py new file mode 100644 index 00000000..d24cf63a --- /dev/null +++ b/myhdl/test/bugs/test_issue_98.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import +from myhdl import * +from myhdl.conversion import analyze + +import pytest + +def issue_98(sda, scl, sda_i, sda_o, scl_i, scl_o): + sda_d, scl_d = sda.driver(), scl.driver() + @always_comb + def hdl(): + sda_i.next = sda + sda_d.next = 0 if not sda_o else None + scl_i.next = scl + scl_d.next = None if not scl_o else 1 + return hdl + +def test_issue_98_1(): + sda_i, sda_o, scl_i, scl_o = [Signal(False) for i in range(4)] + sda, scl = [TristateSignal(False) for i in range(2)] + toVHDL.name = toVerilog.name = 'issue_98_1' + assert analyze(issue_98, sda, scl, sda_i, sda_o, scl_i, scl_o) == 0 + +def test_issue_98_2(): + sda_i, sda_o, scl_i, scl_o = [Signal(intbv(0)[2:0]) for i in range(4)] + sda, scl = [TristateSignal(intbv(0)[2:0]) for i in range(2)] + toVHDL.name = toVerilog.name = 'issue_98_2' + assert analyze(issue_98, sda, scl, sda_i, sda_o, scl_i, scl_o) == 0 + +def test_issue_98_3(): + sda_i, sda_o, scl_i, scl_o = [Signal(intbv(0)[1:0]) for i in range(4)] + sda, scl = [TristateSignal(intbv(0)[1:0]) for i in range(2)] + toVHDL.name = toVerilog.name = 'issue_98_3' + assert analyze(issue_98, sda, scl, sda_i, sda_o, scl_i, scl_o) == 0 + diff --git a/myhdl/test/bugs/vcom.py b/myhdl/test/bugs/vcom.py deleted file mode 100644 index 32f3753b..00000000 --- a/myhdl/test/bugs/vcom.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vcom" diff --git a/myhdl/test/bugs/vlog.py b/myhdl/test/bugs/vlog.py deleted file mode 100644 index 95b433ea..00000000 --- a/myhdl/test/bugs/vlog.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vlog" diff --git a/myhdl/test/conftest.py b/myhdl/test/conftest.py new file mode 100644 index 00000000..2accff93 --- /dev/null +++ b/myhdl/test/conftest.py @@ -0,0 +1,41 @@ +import sys + +import py +import pytest + +from myhdl.conversion import analyze, verify +from myhdl.conversion._verify import _simulators + +xfail = pytest.mark.xfail + +all_sims = list(_simulators) + +if sys.version_info[0] > 2: + collect_ignore = ['conversion/toVerilog/test_not_supported_py2.py'] + +def pytest_addoption(parser): + parser.addoption("--sim", action="store", choices=all_sims, + help="HDL Simulator") + + +def pytest_configure(config): + sim = config.getoption('sim') + if sim is not None: + verify.simulator = analyze.simulator = sim + + +def pytest_report_header(config): + sim = config.getoption('sim') + if config.getoption('sim') is not None: + hdr = ['Simulator: {sim}'] + if not py.path.local.sysfind(sim): + hdr += ['Warning: {sim} not found in PATH'] + return '\n'.join(hdr).format(sim=sim) + + +def bug(issue_no, hdl='all'): + if hdl == 'all': + sims = all_sims + else: + sims = [k for k, v in _simulators.items() if v.hdl.lower() == hdl] + return xfail(verify.simulator in sims, reason='issue '+issue_no) diff --git a/myhdl/test/conversion/Makefile b/myhdl/test/conversion/Makefile index 16e631be..43512b65 100644 --- a/myhdl/test/conversion/Makefile +++ b/myhdl/test/conversion/Makefile @@ -3,13 +3,17 @@ all: general toVerilog2 toVHDL toVerilog general: - cd general; make -k + cd general; py.test --sim ghdl; py.test --sim iverilog toVerilog2: - cd toVerilog2; make -k + cd toVerilog2; py.test --sim iverilog toVHDL: - cd toVHDL; make -k + cd toVHDL; py.test --sim ghdl toVerilog: - cd toVerilog; make -k + cd toVerilog; py.test --sim iverilog + +gitclean: + git clean -dfx + diff --git a/myhdl/test/conversion/conftest.py b/myhdl/test/conversion/conftest.py deleted file mode 100644 index 375e1504..00000000 --- a/myhdl/test/conversion/conftest.py +++ /dev/null @@ -1,20 +0,0 @@ -from itertools import chain - -import pytest - -from myhdl.conversion import verify - -xfail = pytest.mark.xfail - -hdlmap = { - 'verilog': ('icarus', 'vlog'), - 'vhdl': ('GHDL', 'vcom') -} - - -def bug(issue_no, hdl='all'): - if hdl == 'all': - sims = list(chain.from_iterable(hdlmap.values())) - else: - sims = hdlmap[hdl] - return xfail(verify.simulator in sims, reason='issue '+issue_no) diff --git a/myhdl/test/conversion/general/GHDL.py b/myhdl/test/conversion/general/GHDL.py deleted file mode 100644 index ea033d28..00000000 --- a/myhdl/test/conversion/general/GHDL.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "GHDL" diff --git a/myhdl/test/conversion/general/Makefile b/myhdl/test/conversion/general/Makefile index f9837062..dacf8c29 100644 --- a/myhdl/test/conversion/general/Makefile +++ b/myhdl/test/conversion/general/Makefile @@ -1,19 +1,22 @@ all: vlog vcom -GHDL: - py.test GHDL.py test_*.py +ghdl: + py.test --sim ghdl vlog: - py.test vlog.py test_*.py + py.test --sim vlog vcom: - py.test vcom.py test_*.py + py.test --sim vcom -icarus: - py.test icarus.py test_*.py +iverilog: + py.test --sim iverilog cver: - py.test cver.py test_*.py + py.test --sim cver clean: - rm *.o *.out *.v *.vhd *.pyc *~ *.vcd* *.log *_ghdl + +gitclean: + git clean -dfx diff --git a/myhdl/test/conversion/general/__init__.py b/myhdl/test/conversion/general/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/conversion/general/cver.py b/myhdl/test/conversion/general/cver.py deleted file mode 100644 index 98961aae..00000000 --- a/myhdl/test/conversion/general/cver.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "cver" diff --git a/myhdl/test/conversion/general/icarus.py b/myhdl/test/conversion/general/icarus.py deleted file mode 100644 index b13985ce..00000000 --- a/myhdl/test/conversion/general/icarus.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "icarus" diff --git a/myhdl/test/conversion/general/test_interfaces4.py b/myhdl/test/conversion/general/test_interfaces4.py index 85865b11..298b6e6a 100644 --- a/myhdl/test/conversion/general/test_interfaces4.py +++ b/myhdl/test/conversion/general/test_interfaces4.py @@ -2,8 +2,6 @@ from __future__ import absolute_import import sys -import pytest - from myhdl import * from myhdl import ConversionError from myhdl.conversion._misc import _error @@ -11,10 +9,6 @@ from myhdl.conversion import analyze, verify from myhdl import * -from myhdl.test.conversion.conftest import bug - -from myhdl import * - """ This set of tests exercies a peculiar scenario where an expanded interface Signal is flagged as having multiple @@ -110,9 +104,13 @@ def c_testbench_one(): while True: yield delay(3) clock.next = not clock - - expected = (False, False, False, True, True, True, - False, True, False, True) + + # there is an issue when using bools with varialbes and + # VHDL conversion, this might be an expected limitation? + #expected = (False, False, False, True, True, True, + # False, True, False, True) + expected = (0, 0, 0, 1, 1, 1, 0, 1, 0, 1) + ra = reset.active @instance def tbstim(): @@ -141,7 +139,6 @@ def test_one_testbench(): Simulation(c_testbench_one()).run() -@bug('82') def test_one_analyze(): clock = Signal(bool(0)) reset = ResetSignal(0, active=1, async=False) @@ -150,11 +147,10 @@ def test_one_analyze(): analyze(m_top, clock, reset, sdi, sdo) -@bug('82') def test_one_verify(): assert verify(c_testbench_one) == 0 -@bug('82') + def test_conversion(): toVerilog(c_testbench_one) toVHDL(c_testbench_one) @@ -171,4 +167,4 @@ if __name__ == '__main__': test_conversion() print("*** verify testbench conversion and execution") test_one_verify() - \ No newline at end of file + diff --git a/myhdl/test/conversion/general/test_ternary.py b/myhdl/test/conversion/general/test_ternary.py index c8e865f8..ec88343a 100644 --- a/myhdl/test/conversion/general/test_ternary.py +++ b/myhdl/test/conversion/general/test_ternary.py @@ -66,9 +66,11 @@ def TernaryBench(ternary): # uncomment when we have a VHDL-2008 compliant simulator -# def test_ternary1(): -# assert conversion.verify(TernaryBench, ternary1) == 0 +def test_ternary1(): + toVHDL.name = 'ternary1' + assert conversion.verify(TernaryBench, ternary1) == 0 def test_ternary2(): + toVHDL.name = 'ternary2' assert conversion.verify(TernaryBench, ternary2) == 0 diff --git a/myhdl/test/conversion/general/vcom.py b/myhdl/test/conversion/general/vcom.py deleted file mode 100644 index 32f3753b..00000000 --- a/myhdl/test/conversion/general/vcom.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vcom" diff --git a/myhdl/test/conversion/general/vlog.py b/myhdl/test/conversion/general/vlog.py deleted file mode 100644 index 95b433ea..00000000 --- a/myhdl/test/conversion/general/vlog.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vlog" diff --git a/myhdl/test/conversion/toVHDL/GHDL.py b/myhdl/test/conversion/toVHDL/GHDL.py deleted file mode 100644 index c7319a69..00000000 --- a/myhdl/test/conversion/toVHDL/GHDL.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify - -verify.simulator = "GHDL" diff --git a/myhdl/test/conversion/toVHDL/Makefile b/myhdl/test/conversion/toVHDL/Makefile index 9f5eae21..5c9201f9 100644 --- a/myhdl/test/conversion/toVHDL/Makefile +++ b/myhdl/test/conversion/toVHDL/Makefile @@ -1,10 +1,13 @@ all: vcom vcom: - py.test vcom.py test_*.py + py.test --sim vcom -GHDL: - py.test GHDL.py test_*.py +ghdl: + py.test --sim ghdl clean: - rm *.o *.out *.v *.vhd *.pyc *~ *.vcd* *.log *_ghdl + +gitclean: + git clean -dfx diff --git a/myhdl/test/conversion/toVHDL/__init__.py b/myhdl/test/conversion/toVHDL/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/conversion/toVHDL/test_custom.py b/myhdl/test/conversion/toVHDL/test_custom.py index 0ad22592..23bcb98a 100644 --- a/myhdl/test/conversion/toVHDL/test_custom.py +++ b/myhdl/test/conversion/toVHDL/test_custom.py @@ -74,10 +74,10 @@ def inc(count, enable, clock, reset, n): __vhdl__ = \ """ process (%(clock)s, %(reset)s) begin - if (reset = '0') then + if (%(reset)s = '0') then %(count)s <= (others => '0'); elsif rising_edge(%(clock)s) then - if (enable = '1') then + if (%(enable)s = '1') then %(count)s <= (%(count)s + 1) mod %(n)s; end if; end if; @@ -104,11 +104,11 @@ def incErr(count, enable, clock, reset, n): __vhdl__ = \ """ always @(posedge %(clock)s, negedge %(reset)s) begin - if (reset == 0) begin + if (%(reset)s == 0) begin %(count)s <= 0; end else begin - if (enable) begin + if (%(enable)s) begin %(count)s <= (%(countq)s + 1) %% %(n)s; end end @@ -151,10 +151,10 @@ def inc_seq(count, nextCount, enable, clock, reset): __vhdl__ = \ """ process (%(clock)s, %(reset)s) begin - if (reset = '0') then + if (%(reset)s = '0') then %(count)s <= (others => '0'); elsif rising_edge(%(clock)s) then - if (enable = '1') then + if (%(enable)s = '1') then %(count)s <= %(nextCount)s; end if; end if; @@ -215,7 +215,7 @@ def check(count, enable, clock, reset, n): expect = 0 yield reset.posedge # assert count == expect - print count + print(count) while 1: yield clock.posedge if enable: @@ -223,7 +223,7 @@ def check(count, enable, clock, reset, n): yield delay(1) # print "%d count %s expect %s count_v %s" % (now(), count, expect, count_v) # assert count == expect - print count + print(count) return logic diff --git a/myhdl/test/conversion/toVHDL/test_enum.py b/myhdl/test/conversion/toVHDL/test_enum.py index a715c0a0..d62648e2 100644 --- a/myhdl/test/conversion/toVHDL/test_enum.py +++ b/myhdl/test/conversion/toVHDL/test_enum.py @@ -40,23 +40,23 @@ def bench_enum(): a.next = 0xaa b.next = 0x55 yield clock.posedge - print 'a=%s b=%s' % (a, b) + print('a=%s b=%s' % (a, b)) op.next = bitwise_op.BW_AND yield clock.posedge - print c + print(c) op.next = bitwise_op.BW_ANDN yield clock.posedge - print c + print(c) op.next = bitwise_op.BW_OR yield clock.posedge - print c + print(c) op.next = bitwise_op.BW_XOR yield clock.posedge - print c + print(c) raise StopSimulation diff --git a/myhdl/test/conversion/toVHDL/test_loops.py b/myhdl/test/conversion/toVHDL/test_loops.py index ec111995..5fdd51d0 100644 --- a/myhdl/test/conversion/toVHDL/test_loops.py +++ b/myhdl/test/conversion/toVHDL/test_loops.py @@ -34,7 +34,7 @@ def LoopBench(LoopTest): for i in range(100): a.next = data[i] yield delay(10) - print z + print(z) return stimulus, looptest_inst diff --git a/myhdl/test/conversion/toVHDL/test_newcustom.py b/myhdl/test/conversion/toVHDL/test_newcustom.py index 2c1a123f..10e39a16 100644 --- a/myhdl/test/conversion/toVHDL/test_newcustom.py +++ b/myhdl/test/conversion/toVHDL/test_newcustom.py @@ -74,10 +74,10 @@ def inc(count, enable, clock, reset, n): inc.vhdl_code = \ """ process ($clock, $reset) begin - if (reset = '0') then + if ($reset = '0') then $count <= (others => '0'); elsif rising_edge($clock) then - if (enable = '1') then + if ($enable = '1') then $count <= ($count + 1) mod $n; end if; end if; @@ -104,11 +104,11 @@ def incErr(count, enable, clock, reset, n): incErr.vhdl_code = \ """ always @(posedge $clock, negedge $reset) begin - if (reset == 0) begin + if ($reset == 0) begin $count <= 0; end else begin - if (enable) begin + if ($enable) begin $count <= ($countq + 1) %% $n; end end @@ -151,10 +151,10 @@ def inc_seq(count, nextCount, enable, clock, reset): inc_seq.vhdl_code = \ """ process ($clock, $reset) begin - if (reset = '0') then + if ($reset = '0') then $count <= (others => '0'); elsif rising_edge($clock) then - if (enable = '1') then + if ($enable = '1') then $count <= $nextCount; end if; end if; @@ -215,7 +215,7 @@ def check(count, enable, clock, reset, n): expect = 0 yield reset.posedge # assert count == expect - print count + print(count) while 1: yield clock.posedge if enable: @@ -223,7 +223,7 @@ def check(count, enable, clock, reset, n): yield delay(1) # print "%d count %s expect %s count_v %s" % (now(), count, expect, count_v) # assert count == expect - print count + print(count) return logic diff --git a/myhdl/test/conversion/toVHDL/test_ops.py b/myhdl/test/conversion/toVHDL/test_ops.py index 5badb0f6..a41c9db5 100644 --- a/myhdl/test/conversion/toVHDL/test_ops.py +++ b/myhdl/test/conversion/toVHDL/test_ops.py @@ -145,27 +145,27 @@ def binaryBench(m, n): yield left, right yield delay(1) - print Bitand - print Bitor - print Bitxor - print FloorDiv - print LeftShift + print(Bitand) + print(Bitor) + print(Bitxor) + print(FloorDiv) + print(LeftShift) # print Pow, Pow_v - print Modulo - print RightShift - print Mul - print Sub - print Sum - print int(EQ) - print int(NE) - print int(LT) - print int(GT) - print int(LE) - print int(GE) - print int(Booland) - print int(Boolor) + print(Modulo) + print(RightShift) + print(Mul) + print(Sub) + print(Sum) + print(int(EQ)) + print(int(NE)) + print(int(LT)) + print(int(GT)) + print(int(LE)) + print(int(GE)) + print(int(Booland)) + print(int(Boolor)) return binops, stimulus, check @@ -253,11 +253,11 @@ def multiBench(m, n, p): yield argm, argn, argp yield delay(1) - print Bitand - print Bitor - print Bitxor - print int(Booland) - print int(Boolor) + print(Bitand) + print(Bitor) + print(Bitxor) + print(int(Booland)) + print(int(Boolor)) return multiops, stimulus, check @@ -315,8 +315,8 @@ def unaryBench(m): while 1: yield arg yield delay(1) - print int(Not_kw) - print Invert + print(int(Not_kw)) + print(Invert) # check unary operator support in vhdl # print UnaryAdd # print UnarySub @@ -449,16 +449,16 @@ def augmBench(m, n): while True: yield left, right yield delay(1) - print Bitand - print Bitor - print Bitxor - print Sub - print Sum - print FloorDiv - print LeftShift - print Modulo - print Mul - print RightShift + print(Bitand) + print(Bitor) + print(Bitxor) + print(Sub) + print(Sum) + print(FloorDiv) + print(LeftShift) + print(Modulo) + print(Mul) + print(RightShift) return augmops, stimulus, check diff --git a/myhdl/test/conversion/toVHDL/test_signed.py b/myhdl/test/conversion/toVHDL/test_signed.py index 4bfdef3f..e493a4a4 100644 --- a/myhdl/test/conversion/toVHDL/test_signed.py +++ b/myhdl/test/conversion/toVHDL/test_signed.py @@ -163,24 +163,24 @@ def binaryBench(Ll, Ml, Lr, Mr): ## self.assertEqual(Bitor, Bitor_v) ## self.assertEqual(Bitxor, Bitxor_v) ## ## self.assertEqual(FloorDiv, FloorDiv_v) - print LeftShift + print(LeftShift) # print Modulo - print Mul + print(Mul) # self.assertEqual(Pow, Pow_v) - print RightShift - print Sub - print Sum - print Sum1 - print Sum2 - print Sum3 - print int(EQ) - print int(NE) - print int(LT) - print int(GT) - print int(LE) - print int(GE) - print int(BoolAnd) - print int(BoolOr) + print(RightShift) + print(Sub) + print(Sum) + print(Sum1) + print(Sum2) + print(Sum3) + print(int(EQ)) + print(int(NE)) + print(int(LT)) + print(int(GT)) + print(int(LE)) + print(int(GE)) + print(int(BoolAnd)) + print(int(BoolOr)) return binops, stimulus, check @@ -255,9 +255,9 @@ def unaryBench( m): yield arg yield delay(1) # print BoolNot - print Invert + print(Invert) # print UnaryAdd - print UnarySub + print(UnarySub) return unaryops, stimulus, check @@ -399,12 +399,12 @@ def augmBench( Ll, Ml, Lr, Mr): ## self.assertEqual(Bitor, Bitor_v) ## self.assertEqual(Bitxor, Bitxor_v) ## self.assertEqual(FloorDiv, FloorDiv_v) - print LeftShift + print(LeftShift) ## self.assertEqual(Modulo, Modulo_v) - print Mul - print RightShift - print Sub - print Sum + print(Mul) + print(RightShift) + print(Sub) + print(Sum) return augmops, stimulus, check @@ -488,8 +488,8 @@ def expressionsBench(): while 1: yield clk.posedge yield delay(1) - print int(a) - print int(b) + print(int(a)) + print(int(b)) @instance def clkgen(): diff --git a/myhdl/test/conversion/toVHDL/vcom.py b/myhdl/test/conversion/toVHDL/vcom.py deleted file mode 100644 index 32f3753b..00000000 --- a/myhdl/test/conversion/toVHDL/vcom.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vcom" diff --git a/myhdl/test/conversion/toVerilog/__init__.py b/myhdl/test/conversion/toVerilog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/conversion/toVerilog/test_GrayInc.py b/myhdl/test/conversion/toVerilog/test_GrayInc.py index 536c8593..87cde9db 100644 --- a/myhdl/test/conversion/toVerilog/test_GrayInc.py +++ b/myhdl/test/conversion/toVerilog/test_GrayInc.py @@ -6,10 +6,10 @@ from random import randrange from myhdl import * -from test_bin2gray import bin2gray -from test_inc import inc +from .test_bin2gray import bin2gray +from .test_inc import inc -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_NotSupported.py b/myhdl/test/conversion/toVerilog/test_NotSupported.py index 35663b4b..6f24740b 100644 --- a/myhdl/test/conversion/toVerilog/test_NotSupported.py +++ b/myhdl/test/conversion/toVerilog/test_NotSupported.py @@ -48,20 +48,6 @@ class TestNotSupported(unittest.TestCase): return logic self.check(g, z, a) - def testBackquote(self): - a = Signal(bool()) - z = Signal(bool()) - def g(z, a): - @instance - def logic(): - while 1: - yield a - z.next = 1 - `a` - return logic - self.check(g, z, a) - - def testClass(self): a = Signal(bool()) z = Signal(bool()) @@ -102,19 +88,6 @@ class TestNotSupported(unittest.TestCase): return logic self.check(g, z, a) - def testExec(self): - a = Signal(bool()) - z = Signal(bool()) - def g(z, a): - @instance - def logic(): - while 1: - yield a - z.next = 1 - exec "1 + 2" in globals , locals - return logic - self.check(g, z, a) - def testFrom(self): a = Signal(bool()) z = Signal(bool()) diff --git a/myhdl/test/conversion/toVerilog/test_RandomScrambler.py b/myhdl/test/conversion/toVerilog/test_RandomScrambler.py index 7a73f523..4369f99a 100644 --- a/myhdl/test/conversion/toVerilog/test_RandomScrambler.py +++ b/myhdl/test/conversion/toVerilog/test_RandomScrambler.py @@ -10,7 +10,7 @@ import time from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation N = 8 M = 2 ** N @@ -25,7 +25,7 @@ def XorGate(z, a, b, c): return logic def randOthers(i, n): - l = range(n) + l = list(range(n)) l.remove(i) random.shuffle(l) return l[0], l[1] diff --git a/myhdl/test/conversion/toVerilog/test_always_comb.py b/myhdl/test/conversion/toVerilog/test_always_comb.py index 848e05a8..8f6507ac 100644 --- a/myhdl/test/conversion/toVerilog/test_always_comb.py +++ b/myhdl/test/conversion/toVerilog/test_always_comb.py @@ -32,7 +32,7 @@ from unittest import TestCase from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation QUIET = 1 diff --git a/myhdl/test/conversion/toVerilog/test_beh.py b/myhdl/test/conversion/toVerilog/test_beh.py index bc17b83d..cd115985 100644 --- a/myhdl/test/conversion/toVerilog/test_beh.py +++ b/myhdl/test/conversion/toVerilog/test_beh.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_bin2gray.py b/myhdl/test/conversion/toVerilog/test_bin2gray.py index d85ce15f..3b11a3c1 100644 --- a/myhdl/test/conversion/toVerilog/test_bin2gray.py +++ b/myhdl/test/conversion/toVerilog/test_bin2gray.py @@ -6,7 +6,7 @@ from unittest import TestCase from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def bin2gray2(B, G, width): """ Gray encoder. diff --git a/myhdl/test/conversion/toVerilog/test_bugreports.py b/myhdl/test/conversion/toVerilog/test_bugreports.py index 1bacb196..85e1db83 100644 --- a/myhdl/test/conversion/toVerilog/test_bugreports.py +++ b/myhdl/test/conversion/toVerilog/test_bugreports.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from myhdl import * -from util import verilogCompile +from .util import verilogCompile ############################# # bug report (Tom Dillon) @@ -88,7 +88,7 @@ test() # case variable name in embedded FSM #################################### -from test_fsm import FramerCtrl +from .test_fsm import FramerCtrl def mid(SOF, clk, reset_n): t_State = enum('SEARCH', 'CONFIRM', 'SYNC') diff --git a/myhdl/test/conversion/toVerilog/test_custom.py b/myhdl/test/conversion/toVerilog/test_custom.py index 8742f3d4..51e0d871 100644 --- a/myhdl/test/conversion/toVerilog/test_custom.py +++ b/myhdl/test/conversion/toVerilog/test_custom.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation from myhdl import ConversionError from myhdl.conversion._misc import _error diff --git a/myhdl/test/conversion/toVerilog/test_dec.py b/myhdl/test/conversion/toVerilog/test_dec.py index 6032f43f..b949a7a6 100644 --- a/myhdl/test/conversion/toVerilog/test_dec.py +++ b/myhdl/test/conversion/toVerilog/test_dec.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_edge.py b/myhdl/test/conversion/toVerilog/test_edge.py index bdd4a0fa..8c0d036a 100644 --- a/myhdl/test/conversion/toVerilog/test_edge.py +++ b/myhdl/test/conversion/toVerilog/test_edge.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_errors.py b/myhdl/test/conversion/toVerilog/test_errors.py index f5cf1b66..666d1c8a 100644 --- a/myhdl/test/conversion/toVerilog/test_errors.py +++ b/myhdl/test/conversion/toVerilog/test_errors.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +from __future__ import print_function import os path = os.path import unittest @@ -154,7 +155,8 @@ def recursion2(count, enable, clock, reset, n): return logic def h1(n): - return None + pass + # return None def functionNoReturnVal(count, enable, clock, reset, n): @instance @@ -187,34 +189,6 @@ def taskReturnVal(count, enable, clock, reset, n): return logic -def printnlToFile(count, enable, clock, reset, n): - @instance - def logic(): - cnt = intbv(0)[8:] - while 1: - yield clock.posedge, reset.negedge - if reset == ACTIVE_LOW: - count.next = 0 - else: - if enable: - print >> f, count - count.next = count + 1 - return logic - -def printToFile(count, enable, clock, reset, n): - @instance - def logic(): - cnt = intbv(0)[8:] - while 1: - yield clock.posedge, reset.negedge - if reset == ACTIVE_LOW: - count.next = 0 - else: - if enable: - print >> f, count, - count.next = count + 1 - return logic - def listComp1(count, enable, clock, reset, n): @instance def logic(): @@ -451,22 +425,6 @@ class TestErr(TestCase): else: self.fail() - def testPrintnlToFile(self): - try: - self.bench(printnlToFile) - except ConversionError as e: - self.assertEqual(e.kind, _error.NotSupported) - else: - self.fail() - - def testPrintToFile(self): - try: - self.bench(printToFile) - except ConversionError as e: - self.assertEqual(e.kind, _error.NotSupported) - else: - self.fail() - def testListComp1(self): try: self.bench(listComp1) @@ -556,4 +514,3 @@ if __name__ == '__main__': - diff --git a/myhdl/test/conversion/toVerilog/test_fsm.py b/myhdl/test/conversion/toVerilog/test_fsm.py index 9c954cb1..57195b80 100644 --- a/myhdl/test/conversion/toVerilog/test_fsm.py +++ b/myhdl/test/conversion/toVerilog/test_fsm.py @@ -6,7 +6,7 @@ from unittest import TestCase from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation # SEARCH, CONFIRM, SYNC = range(3) ACTIVE_LOW = 0 diff --git a/myhdl/test/conversion/toVerilog/test_hec.py b/myhdl/test/conversion/toVerilog/test_hec.py index eddf2fde..c37e3b7a 100644 --- a/myhdl/test/conversion/toVerilog/test_hec.py +++ b/myhdl/test/conversion/toVerilog/test_hec.py @@ -6,7 +6,7 @@ from random import randrange from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation COSET = 0x55 @@ -128,9 +128,9 @@ def HecCalculator_v(name, hec, header): -headers = [ 0x00000000L, - 0x01234567L, - 0xbac6f4caL +headers = [ 0x00000000, + 0x01234567, + 0xbac6f4ca ] headers.extend([randrange(2**32-1) for i in range(10)]) diff --git a/myhdl/test/conversion/toVerilog/test_inc.py b/myhdl/test/conversion/toVerilog/test_inc.py index 516c9eed..5eea6830 100644 --- a/myhdl/test/conversion/toVerilog/test_inc.py +++ b/myhdl/test/conversion/toVerilog/test_inc.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_inc_initial.py b/myhdl/test/conversion/toVerilog/test_inc_initial.py index cca2367b..43df8a7c 100644 --- a/myhdl/test/conversion/toVerilog/test_inc_initial.py +++ b/myhdl/test/conversion/toVerilog/test_inc_initial.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ACTIVE_LOW, INACTIVE_HIGH = 0, 1 diff --git a/myhdl/test/conversion/toVerilog/test_infer.py b/myhdl/test/conversion/toVerilog/test_infer.py index 9eec891d..7fde4556 100644 --- a/myhdl/test/conversion/toVerilog/test_infer.py +++ b/myhdl/test/conversion/toVerilog/test_infer.py @@ -8,7 +8,7 @@ from myhdl import * from myhdl import ConversionError from myhdl.conversion._misc import _error -from util import setupCosimulation +from .util import setupCosimulation b = c = 2 diff --git a/myhdl/test/conversion/toVerilog/test_loops.py b/myhdl/test/conversion/toVerilog/test_loops.py index e88e2eb8..4964c6e7 100644 --- a/myhdl/test/conversion/toVerilog/test_loops.py +++ b/myhdl/test/conversion/toVerilog/test_loops.py @@ -7,7 +7,7 @@ from random import randrange from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def ForLoop1(a, out): @instance diff --git a/myhdl/test/conversion/toVerilog/test_misc.py b/myhdl/test/conversion/toVerilog/test_misc.py index a233872f..f532e44e 100644 --- a/myhdl/test/conversion/toVerilog/test_misc.py +++ b/myhdl/test/conversion/toVerilog/test_misc.py @@ -6,7 +6,7 @@ from random import randrange from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation ### test of constant wire support ### diff --git a/myhdl/test/conversion/toVerilog/test_newcustom.py b/myhdl/test/conversion/toVerilog/test_newcustom.py index 13695fe2..eb3808c5 100644 --- a/myhdl/test/conversion/toVerilog/test_newcustom.py +++ b/myhdl/test/conversion/toVerilog/test_newcustom.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation from myhdl import ConversionError from myhdl.conversion._misc import _error diff --git a/myhdl/test/conversion/toVerilog/test_not_supported_py2.py b/myhdl/test/conversion/toVerilog/test_not_supported_py2.py new file mode 100644 index 00000000..0826ca41 --- /dev/null +++ b/myhdl/test/conversion/toVerilog/test_not_supported_py2.py @@ -0,0 +1,34 @@ +import pytest +from myhdl import instance, Signal, toVerilog, ConversionError +from myhdl.conversion._misc import _error +from helpers import raises_kind + +def check(*args): + with raises_kind(ConversionError, _error.NotSupported): + toVerilog(*args) + +def test_Backquote(): + a = Signal(bool()) + z = Signal(bool()) + def g(z, a): + @instance + def logic(): + while 1: + yield a + z.next = 1 + `a` + return logic + check(g, z, a) + +def testExec(): + a = Signal(bool()) + z = Signal(bool()) + def g(z, a): + @instance + def logic(): + while 1: + yield a + z.next = 1 + exec "1 + 2" in globals , locals + return logic + check(g, z, a) diff --git a/myhdl/test/conversion/toVerilog/test_ops.py b/myhdl/test/conversion/toVerilog/test_ops.py index 79803ec0..86f996aa 100644 --- a/myhdl/test/conversion/toVerilog/test_ops.py +++ b/myhdl/test/conversion/toVerilog/test_ops.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def binaryOps( diff --git a/myhdl/test/conversion/toVerilog/test_ram.py b/myhdl/test/conversion/toVerilog/test_ram.py index 7bc6fb94..b96cdd5c 100644 --- a/myhdl/test/conversion/toVerilog/test_ram.py +++ b/myhdl/test/conversion/toVerilog/test_ram.py @@ -6,7 +6,7 @@ from unittest import TestCase from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def ram(dout, din, addr, we, clk, depth=128): """ Simple ram model """ diff --git a/myhdl/test/conversion/toVerilog/test_rom.py b/myhdl/test/conversion/toVerilog/test_rom.py index 13343f95..1abfcc1a 100644 --- a/myhdl/test/conversion/toVerilog/test_rom.py +++ b/myhdl/test/conversion/toVerilog/test_rom.py @@ -7,7 +7,7 @@ from random import randrange from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation D = 256 diff --git a/myhdl/test/conversion/toVerilog/test_signed.py b/myhdl/test/conversion/toVerilog/test_signed.py index e14f73fc..7e3d24ed 100644 --- a/myhdl/test/conversion/toVerilog/test_signed.py +++ b/myhdl/test/conversion/toVerilog/test_signed.py @@ -9,7 +9,7 @@ random.seed(2) from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def binaryOps( diff --git a/myhdl/test/conversion/toVerilog/test_tristate.py b/myhdl/test/conversion/toVerilog/test_tristate.py index 79f74090..9ff301a6 100644 --- a/myhdl/test/conversion/toVerilog/test_tristate.py +++ b/myhdl/test/conversion/toVerilog/test_tristate.py @@ -3,7 +3,7 @@ path = os.path import unittest from myhdl import * -from util import setupCosimulation +from .util import setupCosimulation def tristate_obuf(A, Y, OE): '''three-state output buffer''' diff --git a/myhdl/test/conversion/toVerilog2/Makefile b/myhdl/test/conversion/toVerilog2/Makefile index 08cd0117..a5b01780 100644 --- a/myhdl/test/conversion/toVerilog2/Makefile +++ b/myhdl/test/conversion/toVerilog2/Makefile @@ -1,13 +1,13 @@ all: vlog vlog: - py.test vlog.py test_*.py + py.test --sim vlog -icarus: - py.test icarus.py test_*.py +iverilog: + py.test --sim iverilog cver: - py.test cver.py test_*.py + py.test --sim cver clean: - rm *.o *.out *.v *.vhd *.pyc *~ *.vcd* *.log diff --git a/myhdl/test/conversion/toVerilog2/__init__.py b/myhdl/test/conversion/toVerilog2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/conversion/toVerilog2/cver.py b/myhdl/test/conversion/toVerilog2/cver.py deleted file mode 100644 index 98961aae..00000000 --- a/myhdl/test/conversion/toVerilog2/cver.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "cver" diff --git a/myhdl/test/conversion/toVerilog2/icarus.py b/myhdl/test/conversion/toVerilog2/icarus.py deleted file mode 100644 index b13985ce..00000000 --- a/myhdl/test/conversion/toVerilog2/icarus.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "icarus" diff --git a/myhdl/test/conversion/toVerilog2/test_loops.py b/myhdl/test/conversion/toVerilog2/test_loops.py index 201e90d2..96c9fb27 100644 --- a/myhdl/test/conversion/toVerilog2/test_loops.py +++ b/myhdl/test/conversion/toVerilog2/test_loops.py @@ -33,7 +33,7 @@ def LoopBench(LoopTest): for i in range(100): a.next = data[i] yield delay(10) - print z + print(z) return stimulus, looptest_inst diff --git a/myhdl/test/conversion/toVerilog2/vcom.py b/myhdl/test/conversion/toVerilog2/vcom.py deleted file mode 100644 index 32f3753b..00000000 --- a/myhdl/test/conversion/toVerilog2/vcom.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vcom" diff --git a/myhdl/test/conversion/toVerilog2/vlog.py b/myhdl/test/conversion/toVerilog2/vlog.py deleted file mode 100644 index 95b433ea..00000000 --- a/myhdl/test/conversion/toVerilog2/vlog.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from myhdl.conversion import verify, analyze - -verify.simulator = analyze.simulator = "vlog" diff --git a/myhdl/test/core/__init__.py b/myhdl/test/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/myhdl/test/core/test_Cosimulation.py b/myhdl/test/core/test_Cosimulation.py index 2d6ad579..299818bd 100644 --- a/myhdl/test/core/test_Cosimulation.py +++ b/myhdl/test/core/test_Cosimulation.py @@ -20,25 +20,24 @@ """ Run unit tests for Cosimulation """ from __future__ import absolute_import - -import sys +import gc import os -import errno -import unittest -from unittest import TestCase import random -from random import randrange -random.seed(1) # random, but deterministic +import sys + +from myhdl import Signal +from myhdl._compat import to_bytes +from myhdl._Cosimulation import Cosimulation, CosimulationError, _error + +if __name__ != '__main__': + from helpers import raises_kind + +random.seed(1) # random, but deterministic MAXLINE = 4096 -import pytest -from myhdl import Signal -from myhdl._Cosimulation import Cosimulation, CosimulationError, _error -from myhdl._compat import to_bytes, PYPY - -exe = "python test_Cosimulation.py CosimulationTest" +exe = "python {0} ".format(os.path.abspath(__file__)) fromSignames = ['a', 'bb', 'ccc'] fromSizes = [1, 11, 63] @@ -56,27 +55,23 @@ toXVals = ["X00", "FZ3", "34XZ", "56U"] allSigs = fromSigs.copy() allSigs.update(toSigs) -@pytest.mark.xfail(PYPY, reason="This test does not work on pypy") -class CosimulationTest(TestCase): - + +class TestCosimulation: + + def setup_method(self, method): + gc.collect() + def testWrongExe(self): - try: - Cosimulation("bla -x 45") - except CosimulationError as e: - self.assertEqual(e.kind, _error.OSError) - else: - self.fail() + with raises_kind(CosimulationError, _error.OSError): + Cosimulation('bla -x 45') def testNotUnique(self): - cosim1 = Cosimulation(exe + ".cosimNotUnique", **allSigs) - try: - Cosimulation(exe + ".cosimNotUnique", **allSigs) - except CosimulationError as e: - self.assertEqual(e.kind, _error.MultipleCosim) - else: - self.fail() + cosim1 = Cosimulation(exe + "cosimNotUnique", **allSigs) + with raises_kind(CosimulationError, _error.MultipleCosim): + Cosimulation(exe + "cosimNotUnique", **allSigs) - def cosimNotUnique(self): + @staticmethod + def cosimNotUnique(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) os.write(wt, b"TO 00 a 1") @@ -87,11 +82,12 @@ class CosimulationTest(TestCase): os.read(rf, MAXLINE) def testFromSignals(self): - cosim = Cosimulation(exe + ".cosimFromSignals", **allSigs) - self.assertEqual(cosim._fromSignames, fromSignames) - self.assertEqual(cosim._fromSizes, fromSizes) + cosim = Cosimulation(exe + "cosimFromSignals", **allSigs) + assert cosim._fromSignames == fromSignames + assert cosim._fromSizes == fromSizes - def cosimFromSignals(self): + @staticmethod + def cosimFromSignals(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "FROM 00 " @@ -105,13 +101,14 @@ class CosimulationTest(TestCase): os.read(rf, MAXLINE) def testToSignals(self): - cosim = Cosimulation(exe + ".cosimToSignals", **toSigs) - self.assertEqual(cosim._fromSignames, []) - self.assertEqual(cosim._fromSizes, []) - self.assertEqual(cosim._toSignames, toSignames) - self.assertEqual(cosim._toSizes, toSizes) + cosim = Cosimulation(exe + "cosimToSignals", **toSigs) + assert cosim._fromSignames == [] + assert cosim._fromSizes == [] + assert cosim._toSignames == toSignames + assert cosim._toSizes == toSizes - def cosimToSignals(self): + @staticmethod + def cosimToSignals(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "TO 00 " @@ -125,13 +122,14 @@ class CosimulationTest(TestCase): os.read(rf, MAXLINE) def testFromToSignals(self): - cosim = Cosimulation(exe + ".cosimFromToSignals", **allSigs) - self.assertEqual(cosim._fromSignames, fromSignames) - self.assertEqual(cosim._fromSizes, fromSizes) - self.assertEqual(cosim._toSignames, toSignames) - self.assertEqual(cosim._toSizes, toSizes) + cosim = Cosimulation(exe + "cosimFromToSignals", **allSigs) + assert cosim._fromSignames == fromSignames + assert cosim._fromSizes == fromSizes + assert cosim._toSignames == toSignames + assert cosim._toSizes == toSizes - def cosimFromToSignals(self): + @staticmethod + def cosimFromToSignals(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "FROM 00 " @@ -146,16 +144,13 @@ class CosimulationTest(TestCase): os.read(rf, MAXLINE) os.write(wt, b"START") os.read(rf, MAXLINE) - - def testTimeZero(self): - try: - Cosimulation(exe + ".cosimTimeZero", **allSigs) - except CosimulationError as e: - self.assertEqual(e.kind, _error.TimeZero) - except: - self.fail() - def cosimTimeZero(self): + def testTimeZero(self): + with raises_kind(CosimulationError, _error.TimeZero): + Cosimulation(exe + "cosimTimeZero", **allSigs) + + @staticmethod + def cosimTimeZero(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "TO 01 " @@ -164,14 +159,11 @@ class CosimulationTest(TestCase): os.write(wt, to_bytes(buf)) def testNoComm(self): - try: - Cosimulation(exe + ".cosimNoComm", **allSigs) - except CosimulationError as e: - self.assertEqual(e.kind, _error.NoCommunication) - else: - self.fail() - - def cosimNoComm(self): + with raises_kind(CosimulationError, _error.NoCommunication): + Cosimulation(exe + "cosimNoComm", **allSigs) + + @staticmethod + def cosimNoComm(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) os.write(wt, b"FROM 0000") @@ -182,14 +174,11 @@ class CosimulationTest(TestCase): os.read(rf, MAXLINE) def testFromSignalsDupl(self): - try: - Cosimulation(exe + ".cosimFromSignalsDupl", **allSigs) - except CosimulationError as e: - self.assertEqual(e.kind, _error.DuplicateSigNames) - else: - self.fail() + with raises_kind(CosimulationError, _error.DuplicateSigNames): + Cosimulation(exe + "cosimFromSignalsDupl", **allSigs) - def cosimFromSignalsDupl(self): + @staticmethod + def cosimFromSignalsDupl(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "FROM 00 " @@ -199,14 +188,11 @@ class CosimulationTest(TestCase): os.write(wt, to_bytes(buf)) def testToSignalsDupl(self): - try: - Cosimulation(exe + ".cosimToSignalsDupl", **allSigs) - except CosimulationError as e: - self.assertEqual(e.kind, _error.DuplicateSigNames) - else: - self.fail() - - def cosimToSignalsDupl(self): + with raises_kind(CosimulationError, _error.DuplicateSigNames): + Cosimulation(exe + "cosimToSignalsDupl", **allSigs) + + @staticmethod + def cosimToSignalsDupl(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "TO 00 " @@ -216,12 +202,13 @@ class CosimulationTest(TestCase): os.write(wt, to_bytes(buf)) def testFromSignalVals(self): - cosim = Cosimulation(exe + ".cosimFromSignalVals", **allSigs) + cosim = Cosimulation(exe + "cosimFromSignalVals", **allSigs) os.read(cosim._rt, MAXLINE) cosim._hasChange = 1 cosim._put(0) - def cosimFromSignalVals(self): + @staticmethod + def cosimFromSignalVals(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "FROM 00 " @@ -236,23 +223,23 @@ class CosimulationTest(TestCase): os.write(wt, b"DUMMY") s = os.read(rf, MAXLINE) vals = [int(e, 16) for e in s.split()[1:]] - self.assertEqual(vals, fromVals) + assert vals == fromVals def testToSignalVals(self): - cosim = Cosimulation(exe + ".cosimToSignalVals", **allSigs) + cosim = Cosimulation(exe + "cosimToSignalVals", **allSigs) for n in toSignames: - self.assertEqual(toSigs[n].next, 0) + assert toSigs[n].next == 0 cosim._get() for n, v in zip(toSignames, toVals): - self.assertEqual(toSigs[n].next, v) + assert toSigs[n].next == v os.write(cosim._wf, b"DUMMY") cosim._getMode = 1 cosim._get() for n in toSignames: - self.assertEqual(toSigs[n].next, 0) - + assert toSigs[n].next == 0 - def cosimToSignalVals(self): + @staticmethod + def cosimToSignalVals(): wt = int(os.environ['MYHDL_TO_PIPE']) rf = int(os.environ['MYHDL_FROM_PIPE']) buf = "FROM 00 " @@ -283,9 +270,5 @@ class CosimulationTest(TestCase): buf += " " os.write(wt, to_bytes(buf)) -def suite(): - return unittest.makeSuite(CosimulationTest, 'test') - if __name__ == "__main__": - unittest.main() - + getattr(TestCosimulation, sys.argv[1])() diff --git a/myhdl/test/core/test_ShadowSignal.py b/myhdl/test/core/test_ShadowSignal.py index 713643ce..87705dc0 100644 --- a/myhdl/test/core/test_ShadowSignal.py +++ b/myhdl/test/core/test_ShadowSignal.py @@ -1,7 +1,9 @@ from __future__ import absolute_import + from myhdl import * from myhdl._compat import long + def bench_SliceSignal(): s = Signal(intbv(0)[8:]) @@ -55,9 +57,11 @@ def bench_ConcatSignal(): return check + def test_ConcatSignal(): Simulation(bench_ConcatSignal()).run() + def bench_ConcatSignalWithConsts(): a = Signal(intbv(0)[5:]) @@ -100,9 +104,45 @@ def bench_ConcatSignalWithConsts(): return check + def test_ConcatSignalWithConsts(): Simulation(bench_ConcatSignalWithConsts()).run() +def bench_ConcatSignalWithNegs(): + + Am = 2**(5-1) + Cm = 2**(3-1) + Dm = 2**(4-1) + + a = Signal(intbv(-1, min=-Am, max=Am)) + b = Signal(bool(0)) + c = Signal(intbv(-1, min=-Cm, max=Cm)) + d = Signal(intbv(-1, min=-Dm, max=Dm)) + + s = ConcatSignal(a, b, c, d) + + @instance + def check(): + for i in range(-Am, Am): + for j in (0, 1): + for k in range(-Cm, Cm): + for m in range(-Dm, Dm): + a.next = i + b.next = j + c.next = k + d.next = m + yield delay(10) + + assert s[13:8] == a[len(a):] + assert s[7] == b + assert s[7:4] == c[len(c):] + assert s[4:] == d[len(d):] + + return check + +def test_ConcatSignalWithNegs(): + Simulation(bench_ConcatSignalWithNegs()).run() + def bench_TristateSignal(): s = TristateSignal(intbv(0)[8:]) @@ -133,4 +173,3 @@ def bench_TristateSignal(): def test_TristateSignal(): Simulation(bench_TristateSignal()).run() - diff --git a/myhdl/test/core/test_Signal.py b/myhdl/test/core/test_Signal.py index a37d0be2..1da94f2e 100644 --- a/myhdl/test/core/test_Signal.py +++ b/myhdl/test/core/test_Signal.py @@ -20,28 +20,26 @@ """ Run the unit tests for Signal """ from __future__ import absolute_import - +import copy import operator import random -from random import randrange -random.seed(1) # random, but deterministic import sys -maxint = sys.maxsize -import types -import copy +from random import randrange -import unittest -from unittest import TestCase +import pytest -from myhdl._simulator import _siglist -from myhdl import intbv, Signal +from myhdl import Signal, intbv from myhdl._compat import long +from myhdl._simulator import _siglist - -class SigTest(TestCase): +random.seed(1) # random, but deterministic +maxint = sys.maxsize - def setUp(self): - self.vals = [0, 0, 1, 1, 1, 2, 3, 5, intbv(0), intbv(1), intbv(2)] + +class TestSig: + + def setup_method(self, method): + self.vals = [0, 0, 1, 1, 1, 2, 3, 5, intbv(0), intbv(1), intbv(2)] self.nexts = [0, 1, 1, 0, 1, 0, 4, 5, intbv(1), intbv(0), intbv(0)] self.vals += [intbv(0), intbv(1), intbv(0), intbv(1), 2 ] self.nexts += [intbv(0), intbv(1), 1 , 0 , intbv(3) ] @@ -50,65 +48,48 @@ class SigTest(TestCase): self.vals += [bool(0), bool(1), bool(0), bool(1), bool(0), bool(1)] self.nexts += [bool(0), bool(1), bool(1), bool(0), 1 , 0 ] self.sigs = [Signal(i) for i in self.vals] - + self.incompatibleVals = [ [3, 4], (1, 2), 3 , intbv(0), [1] ] self.incompatibleNexts = [ 4 , 3 , "3", (0) , intbv(1) ] self.incompatibleSigs = [Signal(i) for i in self.incompatibleVals] - + self.eventWaiters = [object() for i in range(3)] self.posedgeWaiters = [object() for i in range(5)] self.negedgeWaiters = [object() for i in range(7)] - def testValAttrReadOnly(self): """ val attribute should not be writable""" s1 = Signal(1) - try: + with pytest.raises(AttributeError): s1.val = 1 - except AttributeError: - pass - else: - self.fail() def testDrivenAttrValue(self): """ driven attribute only accepts value 'reg' or 'wire' """ s1 = Signal(1) - try: + with pytest.raises(ValueError): s1.driven = "signal" - except ValueError: - pass - else: - self.fail() - + def testPosedgeAttrReadOnly(self): """ posedge attribute should not be writable""" s1 = Signal(1) - try: + with pytest.raises(AttributeError): s1.posedge = 1 - except AttributeError: - pass - else: - self.fail() - + def testNegedgeAttrReadOnly(self): """ negedge attribute should not be writable""" s1 = Signal(1) - try: + with pytest.raises(AttributeError): s1.negedge = 1 - except AttributeError: - pass - else: - self.fail() def testInitDefault(self): """ initial value is None by default """ s1 = Signal() - self.assertEqual(s1, None) + assert s1 == None def testInitialization(self): """ initial val and next should be equal """ for s in self.sigs: - self.assertEqual(s.val, s.next) + assert s.val == s.next def testUpdate(self): """ _update() should assign next into val """ @@ -116,38 +97,35 @@ class SigTest(TestCase): cur = copy.copy(s.val) s.next = n # assigning to next should not change current value ... - self.assertTrue(s.val == cur) + assert s.val == cur s._update() - self.assertTrue(s.val == n) + assert s.val == n def testNextType(self): """ sig.next = n should fail on access if type(n) incompatible """ i = 0 for s in (self.sigs + self.incompatibleSigs): for n in (self.vals + self.incompatibleVals): - self.assertTrue(isinstance(s.val, s._type)) + assert isinstance(s.val, s._type) if isinstance(s.val, (int, long, intbv)): t = (int, long, intbv) else: t = s._type if not isinstance(n, t): i += 1 - try: + with pytest.raises((TypeError, ValueError)): oldval = s.val s.next = n - except (TypeError, ValueError): - pass - else: - self.fail() - self.assertTrue(i >= len(self.incompatibleSigs), "Nothing tested %s" %i) + + assert i >= len(self.incompatibleSigs), "Nothing tested %s" %i def testAfterUpdate(self): """ updated val and next should be equal but not identical """ for s, n in zip(self.sigs, self.nexts): s.next = n s._update() - self.assertEqual(s.val, s.next) - + assert s.val == s.next + def testModify(self): """ Modifying mutable next should be on a copy """ for s in self.sigs: @@ -163,8 +141,8 @@ class SigTest(TestCase): elif type(s.val) is dict: s.next[3] = 5 else: - s.next # plain read access - self.assertTrue(s.val is not s.next, repr(s.val)) + s.next # plain read access + assert s.val is not s.next, repr(s.val) def testUpdatePosedge(self): """ update on posedge should return event and posedge waiters """ @@ -177,11 +155,11 @@ class SigTest(TestCase): s1._negedgeWaiters = self.negedgeWaiters[:] waiters = s1._update() expected = self.eventWaiters + self.posedgeWaiters - self.assertEqual(set(waiters), set(expected)) - self.assertEqual(s1._eventWaiters, []) - self.assertEqual(s1._posedgeWaiters, []) - self.assertEqual(s1._negedgeWaiters, self.negedgeWaiters) - + assert set(waiters) == set(expected) + assert s1._eventWaiters == [] + assert s1._posedgeWaiters == [] + assert s1._negedgeWaiters == self.negedgeWaiters + def testUpdateNegedge(self): """ update on negedge should return event and negedge waiters """ s1 = Signal(1) @@ -193,10 +171,10 @@ class SigTest(TestCase): s1._negedgeWaiters = self.negedgeWaiters[:] waiters = s1._update() expected = self.eventWaiters + self.negedgeWaiters - self.assertEqual(set(waiters), set(expected)) - self.assertEqual(s1._eventWaiters, []) - self.assertEqual(s1._posedgeWaiters, self.posedgeWaiters) - self.assertEqual(s1._negedgeWaiters, []) + assert set(waiters) == set(expected) + assert s1._eventWaiters == [] + assert s1._posedgeWaiters == self.posedgeWaiters + assert s1._negedgeWaiters == [] def testUpdateEvent(self): """ update on non-edge event should return event waiters """ @@ -209,11 +187,11 @@ class SigTest(TestCase): s1._negedgeWaiters = self.negedgeWaiters[:] waiters = s1._update() expected = self.eventWaiters - self.assertEqual(set(waiters), set(expected)) - self.assertEqual(s1._eventWaiters, []) - self.assertEqual(s1._posedgeWaiters, self.posedgeWaiters) - self.assertEqual(s1._negedgeWaiters, self.negedgeWaiters) - + assert set(waiters) == set(expected) + assert s1._eventWaiters == [] + assert s1._posedgeWaiters == self.posedgeWaiters + assert s1._negedgeWaiters == self.negedgeWaiters + def testUpdateNoEvent(self): """ update without value change should not return event waiters """ s1 = Signal(1) @@ -224,28 +202,28 @@ class SigTest(TestCase): s1._posedgeWaiters = self.posedgeWaiters[:] s1._negedgeWaiters = self.negedgeWaiters[:] waiters = s1._update() - self.assertEqual(waiters, []) - self.assertEqual(s1._eventWaiters, self.eventWaiters) - self.assertEqual(s1._posedgeWaiters, self.posedgeWaiters) - self.assertEqual(s1._negedgeWaiters, self.negedgeWaiters) - + assert waiters == [] + assert s1._eventWaiters == self.eventWaiters + assert s1._posedgeWaiters == self.posedgeWaiters + assert s1._negedgeWaiters == self.negedgeWaiters + def testNextAccess(self): """ each next attribute access puts a sig in a global siglist """ del _siglist[:] s = [None] * 4 for i in range(len(s)): s[i] = Signal(i) - s[1].next # read access + s[1].next # read access s[2].next = 1 s[2].next s[3].next = 0 s[3].next = 1 s[3].next = 3 for i in range(len(s)): - self.assertEqual(_siglist.count(s[i]), i) - - -class TestSignalAsNum(TestCase): + assert _siglist.count(s[i]) == i + + +class TestSignalAsNum: def seqSetup(self, imin, imax, jmin=0, jmax=None): seqi = [imin, imin, 12, 34] @@ -279,7 +257,7 @@ class TestSignalAsNum(TestCase): seqj.append(j) self.seqi = seqi self.seqj = seqj - + def binaryCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) for i, j in zip(self.seqi, self.seqj): @@ -289,12 +267,12 @@ class TestSignalAsNum(TestCase): r1 = op(bi, j) r2 = op(long(i), bj) r3 = op(bi, bj) - self.assertEqual(type(r1), type(ref)) - self.assertEqual(type(r2), type(ref)) - self.assertEqual(type(r3), type(ref)) - self.assertEqual(r1, ref) - self.assertEqual(r2, ref) - self.assertEqual(r3, ref) + assert type(r1) == type(ref) + assert type(r2) == type(ref) + assert type(r3) == type(ref) + assert r1 == ref + assert r2 == ref + assert r3 == ref def augmentedAssignCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) @@ -303,41 +281,35 @@ class TestSignalAsNum(TestCase): ref = long(i) ref = op(ref, j) r1 = bi1 = Signal(i) - try: + with pytest.raises(TypeError): r1 = op(r1, j) - except TypeError: - pass - else: - self.fail() + r2 = long(i) r2 = op(r2, bj) r3 = bi3 = Signal(i) - try: + with pytest.raises(TypeError): r3 = op(r3, bj) - except TypeError: - pass - else: - self.fail() - self.assertEqual(r2, ref) - + + assert r2 == ref + def unaryCheck(self, op, imin=0, imax=None): self.seqSetup(imin=imin, imax=imax) for i in self.seqi: bi = Signal(i) ref = op(i) r1 = op(bi) - self.assertEqual(type(r1), type(ref)) - self.assertEqual(r1, ref) - + assert type(r1) == type(ref) + assert r1 == ref + def conversionCheck(self, op, imin=0, imax=None): self.seqSetup(imin=imin, imax=imax) for i in self.seqi: bi = Signal(i) ref = op(i) r1 = op(bi) - self.assertEqual(type(r1), type(ref)) - self.assertEqual(r1, ref) - + assert type(r1) == type(ref) + assert r1 == ref + def comparisonCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) for i, j in zip(self.seqi, self.seqj): @@ -347,9 +319,9 @@ class TestSignalAsNum(TestCase): r1 = op(bi, j) r2 = op(i, bj) r3 = op(bi, bj) - self.assertEqual(r1, ref) - self.assertEqual(r2, ref) - self.assertEqual(r3, ref) + assert r1 == ref + assert r2 == ref + assert r3 == ref def testAdd(self): self.binaryCheck(operator.add) @@ -358,7 +330,7 @@ class TestSignalAsNum(TestCase): self.binaryCheck(operator.sub) def testMul(self): - self.binaryCheck(operator.mul, imax=maxint) # XXX doesn't work for long i??? + self.binaryCheck(operator.mul, imax=maxint) # XXX doesn't work for long i??? def testFloorDiv(self): self.binaryCheck(operator.floordiv, jmin=1) @@ -391,7 +363,7 @@ class TestSignalAsNum(TestCase): self.augmentedAssignCheck(operator.isub) def testIMul(self): - self.augmentedAssignCheck(operator.imul, imax=maxint) #XXX doesn't work for long i??? + self.augmentedAssignCheck(operator.imul, imax=maxint) # XXX doesn't work for long i??? def testIFloorDiv(self): self.augmentedAssignCheck(operator.ifloordiv, jmin=1) @@ -419,7 +391,7 @@ class TestSignalAsNum(TestCase): def testNeg(self): self.unaryCheck(operator.neg) - + def testNeg(self): self.unaryCheck(operator.pos) @@ -431,31 +403,36 @@ class TestSignalAsNum(TestCase): def testInt(self): self.conversionCheck(int, imax=maxint) - + def testLong(self): self.conversionCheck(long) - + def testFloat(self): self.conversionCheck(float) # XXX __complex__ seems redundant ??? (complex() works as such?) - + def testOct(self): self.conversionCheck(oct) - + def testHex(self): self.conversionCheck(hex) def testLt(self): self.comparisonCheck(operator.lt) + def testLe(self): self.comparisonCheck(operator.le) + def testGt(self): self.comparisonCheck(operator.gt) + def testGe(self): self.comparisonCheck(operator.ge) + def testEq(self): self.comparisonCheck(operator.eq) + def testNe(self): self.comparisonCheck(operator.ne) @@ -466,6 +443,7 @@ def getItem(s, i): si = len(exts)-1-i return exts[si] + def getSlice(s, i, j): ext = '0' * (i-len(s)+1) exts = ext + s @@ -474,8 +452,7 @@ def getSlice(s, i, j): return exts[si:sj] - -class TestSignalIntBvIndexing(TestCase): +class TestSignalIntBvIndexing: def seqsSetup(self): seqs = ["0", "1", "000", "111", "010001", "110010010", "011010001110010"] @@ -499,10 +476,10 @@ class TestSignalIntBvIndexing(TestCase): ref = long(getItem(s, i), 2) res = sbv[i] resi = sbvi[i] - self.assertEqual(res, ref) - self.assertEqual(type(res), bool) - self.assertEqual(resi, ref^1) - self.assertEqual(type(resi), bool) + assert res == ref + assert type(res) == bool + assert resi == ref^1 + assert type(resi) == bool def testGetSlice(self): self.seqsSetup() @@ -516,88 +493,74 @@ class TestSignalIntBvIndexing(TestCase): res = sbv[i:j] resi = sbvi[i:j] except ValueError: - self.assertTrue(i<=j) + assert i<=j continue ref = long(getSlice(s, i, j), 2) - self.assertEqual(res, ref) - self.assertEqual(type(res), intbv) + assert res == ref + assert type(res) == intbv mask = (2**(i-j))-1 - self.assertEqual(resi, ref ^ mask) - self.assertEqual(type(resi), intbv) + assert resi == ref ^ mask + assert type(resi) == intbv def testSetItem(self): sbv = Signal(intbv(5)) - try: + with pytest.raises(TypeError): sbv[1] = 1 - except TypeError: - pass - else: - self.fail() - + def testSetSlice(self): sbv = Signal(intbv(5)) - try: + with pytest.raises(TypeError): sbv[1:0] = 1 - except TypeError: - pass - else: - self.fail() -class TestSignalNrBits(TestCase): +class TestSignalNrBits: def testBool(self): - if type(bool) is not type : # bool not a type in 2.2 + if type(bool) is not type : # bool not a type in 2.2 return s = Signal(bool()) - self.assertEqual(s._nrbits, 1) + assert s._nrbits == 1 def testIntbvSlice(self): for n in range(1, 40): for m in range(0, n): s = Signal(intbv()[n:m]) - self.assertEqual(s._nrbits, n-m) + assert s._nrbits == n-m def testIntbvBounds(self): for n in range(1, 40): s = Signal(intbv(min=-(2**n))) - self.assertEqual(s._nrbits, 0) + assert s._nrbits == 0 s = Signal(intbv(max=2**n)) - self.assertEqual(s._nrbits, 0) + assert s._nrbits == 0 s = Signal(intbv(min=0, max=2**n)) - self.assertEqual(s._nrbits, n) + assert s._nrbits == n s = Signal(intbv(1, min=1, max=2**n)) - self.assertEqual(s._nrbits, n) + assert s._nrbits == n s = Signal(intbv(min=0, max=2**n+1)) - self.assertEqual(s._nrbits, n+1) + assert s._nrbits == n+1 s = Signal(intbv(min=-(2**n), max=2**n-1)) - self.assertEqual(s._nrbits, n+1) + assert s._nrbits == n+1 s = Signal(intbv(min=-(2**n), max=1)) - self.assertEqual(s._nrbits, n+1) + assert s._nrbits == n+1 s = Signal(intbv(min=-(2**n)-1, max=2**n-1)) - self.assertEqual(s._nrbits, n+2) - + assert s._nrbits == n+2 + + +class TestSignalBoolBounds: -class TestSignalBoolBounds(TestCase): - def testSignalBoolBounds(self): - if type(bool) is not type: # bool not a type in 2.2 + if type(bool) is not type: # bool not a type in 2.2 return s = Signal(bool()) s.next = 1 s.next = 0 for v in (-1, -8, 2, 5): - try: + with pytest.raises(ValueError): s.next = v - #s._update() - #s.val - except ValueError: - pass - else: - self.fail() - -class TestSignalIntbvBounds(TestCase): + +class TestSignalIntbvBounds: def testSliceAssign(self): s = Signal(intbv(min=-24, max=34)) @@ -605,28 +568,15 @@ class TestSignalIntbvBounds(TestCase): for k in (6, 9, 10): s.next[:] = 0 s.next[k:] = i - self.assertEqual(s.next, i) + assert s.next == i for i in (-25, -128, 34, 35, 229): for k in (0, 9, 10): - try: + with pytest.raises(ValueError): s.next[k:] = i - # s._update() - except ValueError: - pass - else: - self.fail() + s = Signal(intbv(5)[8:]) for v in (0, 2**8-1, 100): s.next[:] = v for v in (-1, 2**8, -10, 1000): - try: + with pytest.raises(ValueError): s.next[:] = v - # s._update() - except ValueError: - pass - else: - self.fail() - - -if __name__ == "__main__": - unittest.main() diff --git a/myhdl/test/core/test_Simulation.py b/myhdl/test/core/test_Simulation.py index 7fce45c9..f9d57757 100644 --- a/myhdl/test/core/test_Simulation.py +++ b/myhdl/test/core/test_Simulation.py @@ -20,45 +20,39 @@ """ Run unit tests for Simulation """ from __future__ import absolute_import - -import unittest -from unittest import TestCase import random from random import randrange -random.seed(1) # random, but deterministic +from unittest import TestCase -from myhdl import Simulation, SimulationError, now, delay, StopSimulation, join -from myhdl import Signal, intbv +from myhdl import (Signal, Simulation, SimulationError, StopSimulation, delay, + intbv, join, now) from myhdl._Simulation import _error +from helpers import raises_kind + +random.seed(1) # random, but deterministic -from myhdl._simulator import _siglist QUIET=1 + class Shared: pass + class SimArgs(TestCase): """ Simulation arguments """ + def test1(self): - try: + with raises_kind(SimulationError, _error.ArgType): Simulation(None) - except SimulationError as e: - self.assertEqual(e.kind, _error.ArgType) - except: - self.fail() def test2(self): def g(): yield delay(10) i = g() - try: + with raises_kind(SimulationError, _error.DuplicatedArg): Simulation(i, i) - except SimulationError as e: - self.assertEqual(e.kind, _error.DuplicatedArg) - except: - self.fail() - + class YieldNone(TestCase): """ Basic test of yield None behavior """ @@ -69,11 +63,11 @@ class YieldNone(TestCase): yield delay(10) a.next = 1 yield None - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield delay(0) - self.assertEqual(a.val, 1) - self.assertEqual(now(), 10) + assert a.val == 1 + assert now() == 10 Simulation(stimulus()).run(quiet=QUIET) def test2(self): @@ -81,19 +75,19 @@ class YieldNone(TestCase): a = Signal(0) yield delay(10) a.next = 1 - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield None a.next = 0 - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield None a.next = 1 - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield delay(0) - self.assertEqual(a.val, 1) - self.assertEqual(now(), 10) + assert a.val == 1 + assert now() == 10 Simulation(stimulus()).run(quiet=QUIET) def test3(self): @@ -102,49 +96,51 @@ class YieldNone(TestCase): yield delay(10) a.next = 1 yield None, delay(10) - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield delay(0) - self.assertEqual(a.val, 1) - self.assertEqual(now(), 10) + assert a.val == 1 + assert now() == 10 Simulation(stimulus()).run(quiet=QUIET) def test4(self): def stimulus(): a = Signal(0) yield delay(10) + def gen(): yield delay(20) a.next = 1 yield None, gen() - self.assertEqual(a.val, 0) - self.assertEqual(now(), 10) + assert a.val == 0 + assert now() == 10 yield delay(25) - self.assertEqual(a.val, 1) + assert a.val == 1 Simulation(stimulus()).run(quiet=QUIET) - + class JoinMix(TestCase): """ Test of joins mixed with other clauses """ - + def test1(self): def stimulus(): a = Signal(0) + def gen(): yield join(delay(10), delay(20)) yield gen(), delay(5) - self.assertEqual(now(), 5) + assert now() == 5 yield a - self.fail("Incorrect run") # should not get here + raise AssertionError("Incorrect run") # should not get here Simulation(stimulus()).run(quiet=QUIET) - + def test2(self): def stimulus(): a = Signal(0) yield join(delay(10), delay(20)), delay(5) - self.assertEqual(now(), 5) + assert now() == 5 yield a - self.fail("Incorrect run") # should not get here + raise AssertionError("Incorrect run") # should not get here Simulation(stimulus()).run(quiet=QUIET) def stimulus(self, a, b, c, d): @@ -162,75 +158,84 @@ class JoinMix(TestCase): b.next = 1 c.next = 0 d.next = 1 - + def test3(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, b, c, d) - self.assertEqual(now(), 20) + assert now() == 20 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) - + def test4(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, b), join(c, d) - self.assertEqual(now(), 10) + assert now() == 10 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) - + def test5(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a), b, join(c, d) - self.assertEqual(now(), 5) + assert now() == 5 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) - + def test6(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, delay(20)), b, join(c, d) - self.assertEqual(now(), 10) + assert now() == 10 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) def test7(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, delay(30)), join(c, d) - self.assertEqual(now(), 20) + assert now() == 20 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) def test8(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, a.negedge) - self.assertEqual(now(), 10) + assert now() == 10 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) def test9(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, a.negedge, c.posedge) - self.assertEqual(now(), 15) + assert now() == 15 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) def test10(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, a) - self.assertEqual(now(), 5) + assert now() == 5 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) - + def test11(self): a, b, c, d = [Signal(0) for i in range(4)] + def response(): yield join(a, b.posedge, b.negedge, a) - self.assertEqual(now(), 15) + assert now() == 15 Simulation(self.stimulus(a, b, c, d), response()).run(quiet=QUIET) - + class JoinedGen(TestCase): - + """ Basic test of yielding joined concurrent generators """ - + def bench(self): clk = Signal(0) sig1 = Signal(0) @@ -251,20 +256,20 @@ class JoinedGen(TestCase): sig1.next = 0 sig2.next = 0 yield join(delay(n0*td), gen(sig1, n1), gen(sig2, n2)) - self.assertEqual(sig1.val, 1) - self.assertEqual(sig2.val, 1) - self.assertEqual(now(), offset + td * max(n0, n1, n2)) + assert sig1.val == 1 + assert sig2.val == 1 + assert now() == offset + td * max(n0, n1, n2) raise StopSimulation("Joined concurrent generator yield") def testYieldJoinedGen(self): Simulation(self.bench()).run(quiet=QUIET) - + class SignalUpdateFirst(TestCase): - + """ Check that signal updates are done first, as in VHDL """ - + def bench(self): Q = Signal(0, delay=9) @@ -280,25 +285,25 @@ class SignalUpdateFirst(TestCase): R.next = 1 S.next = 1 yield delay(10) - self.assertEqual(Q.val, 1) # control - self.assertEqual(R.val, 1) # actual check - self.assertEqual(S.val, 0) # control + assert Q.val == 1 # control + assert R.val == 1 # actual check + assert S.val == 0 # control yield delay(1) - self.assertEqual(Q.val, 1) # control - self.assertEqual(R.val, 1) # control - self.assertEqual(S.val, 1) # control + assert Q.val == 1 # control + assert R.val == 1 # control + assert S.val == 1 # control raise StopSimulation("Signal update test") return process() def testSignalUpdateFirst(self): Simulation(self.bench()).run(quiet=QUIET) - - + + class YieldZeroDelay(TestCase): - + """ Basic test of yielding a zero delay """ - + def bench(self): clk = Signal(0) sig1 = Signal(0) @@ -314,17 +319,17 @@ class YieldZeroDelay(TestCase): for i in range(100): offset = now() n1 = randrange(2, 10) - n2 = randrange(n1+1, 20) # n2 > n1 + n2 = randrange(n1+1, 20) # n2 > n1 yield delay(0), gen(sig1, n1), gen(sig2, n2) - self.assertEqual(sig1.val, 0) - self.assertEqual(sig2.val, 0) - self.assertEqual(now(), offset + 0) + assert sig1.val == 0 + assert sig2.val == 0 + assert now() == offset + 0 yield sig1.posedge - self.assertEqual(sig2.val, 0) - self.assertEqual(now(), offset + n1*td) + assert sig2.val == 0 + assert now() == offset + n1*td yield sig2.posedge - self.assertEqual(now(), offset + n2*td) - + assert now() == offset + n2*td + raise StopSimulation("Zero delay yield") def testYieldZeroDelay(self): @@ -332,9 +337,9 @@ class YieldZeroDelay(TestCase): class YieldConcurrentGen(TestCase): - + """ Basic test of yielding concurrent generators """ - + def bench(self): clk = Signal(0) sig1 = Signal(0) @@ -350,28 +355,27 @@ class YieldConcurrentGen(TestCase): for i in range(100): offset = now() n1 = randrange(2, 10) - n2 = randrange(n1+1, 20) # n2 > n1 + n2 = randrange(n1+1, 20) # n2 > n1 yield delay(td), gen(sig1, n1), gen(sig2, n2) - self.assertEqual(sig1.val, 0) - self.assertEqual(sig2.val, 0) - self.assertEqual(now(), offset + td) + assert sig1.val == 0 + assert sig2.val == 0 + assert now() == offset + td yield sig1.posedge - self.assertEqual(sig2.val, 0) - self.assertEqual(now(), offset + n1*td) + assert sig2.val == 0 + assert now() == offset + n1*td yield sig2.posedge - self.assertEqual(now(), offset + n2*td) + assert now() == offset + n2*td raise StopSimulation("Concurrent generator yield") def testYieldConcurrentGen(self): Simulation(self.bench()).run(quiet=QUIET) - class YieldGen(TestCase): - + """ Basic test of yielding generators """ - + def bench(self): clk = Signal(0) @@ -388,7 +392,7 @@ class YieldGen(TestCase): l.append(e) expectedCnt += e expected.append(expectedCnt) - nlists.append(l) + nlists.append(l) def clkGen(): while 1: @@ -402,15 +406,15 @@ class YieldGen(TestCase): for i in range(n): yield clk.posedge shared.cnt += 1 - self.assertEqual(shared.cnt, expected[shared.i]) + assert shared.cnt == expected[shared.i] shared.i += 1 if nlist: yield task(nlist) - + def module(): for nlist in nlists: yield task(nlist) - self.assertEqual(shared.cnt, expected[-1]) + assert shared.cnt == expected[-1] raise StopSimulation("Generator yield") return(module(), clkGen()) @@ -420,7 +424,7 @@ class YieldGen(TestCase): class DeltaCycleOrder(TestCase): - + """ Check that delta cycle order does not matter """ def bench(self, function): @@ -469,7 +473,7 @@ class DeltaCycleOrder(TestCase): random.shuffle(index) yield clk.posedge yield clk.posedge - self.assertEqual(z.val, function(v[0], v[1], v[2], v[3])) + assert z.val == function(v[0], v[1], v[2], v[3]) raise StopSimulation("Delta cycle order") inputGen = [inGen(i) for i in range(4)] @@ -480,12 +484,12 @@ class DeltaCycleOrder(TestCase): def andFunction(a, b, c, d): return a & b & c & d Simulation(self.bench(andFunction)).run(quiet=QUIET) - + def testOr(self): def orFunction(a, b, c, d): return a | b | c | d Simulation(self.bench(orFunction)).run(quiet=QUIET) - + def testXor(self): def xorFunction(a, b, c, d): return a ^ b ^ c ^ d @@ -503,14 +507,14 @@ class DeltaCycleOrder(TestCase): def function(a, b, c, d): return not (a & (not b)) | ((not c) & d) Simulation(self.bench(function)).run(quiet=QUIET) - + class DeltaCycleRace(TestCase): - + """ Check that delta cycle races are like in VHDL """ - + def bench(self): - + uprange = range(300) msig = Signal(uprange[0]) ssig = [Signal(uprange[-1]) for i in range(2)] @@ -519,35 +523,35 @@ class DeltaCycleRace(TestCase): deltaClk = Signal(0) shared = Shared() shared.t = now() - + def clkGen(): for i in uprange[:-1]: yield delay(10) clk.next = 1 yield delay(10) clk.next = 0 - + def deltaClkGen(): while 1: yield clk deltaClk.next = clk.val - + def master(): i = 0 while 1: yield clk.posedge msig.next = uprange[i+1] - self.assertEqual(msig.val, uprange[i]) + assert msig.val == uprange[i] shared.t = now() i += 1 - + def slave(ssig): """ Double-check proper operation """ i = 0 while 1: yield clk.posedge ssig.next = msig.val - self.assertEqual(ssig.val, uprange[i-1]) + assert ssig.val == uprange[i-1] i += 1 def deltaSlave(dsig): @@ -556,15 +560,14 @@ class DeltaCycleRace(TestCase): while 1: yield deltaClk.posedge dsig.next = msig.val - self.assertEqual(now(), shared.t) - self.assertEqual(dsig.val, uprange[i]) + assert now() == shared.t + assert dsig.val == uprange[i] i += 1 - + return (slave(ssig[1]), deltaSlave(dsig[1]), master(), clkGen(), deltaClkGen(), slave(ssig[0]), deltaSlave(dsig[0])) - - + def testDeltaCycleRace(self): """ Check delta cycle races """ bench = self.bench() @@ -572,32 +575,32 @@ class DeltaCycleRace(TestCase): class DelayLine(TestCase): - + """ Check that delay lines work properly """ def bench(self): uprange = range(500) sig_Z = [Signal(uprange[-i]) for i in range(7)] clk = Signal(0) - + def clkGen(): for i in uprange[:-1]: yield delay(10) clk.next = 1 yield delay(10) clk.next = 0 - + def delayElement(n, i): sig_Z[n].next = sig_Z[n-1].val - self.assertEqual(sig_Z[n].val, uprange[i-n]) - + assert sig_Z[n].val == uprange[i-n] + def stage(n): i = 0 while 1: yield clk.posedge delayElement(n, i) i += 1 - + def stage012(): i = 0 while 1: @@ -606,16 +609,15 @@ class DelayLine(TestCase): sig_Z[0].next = uprange[i+1] delayElement(2, i) i += 1 - + return [stage(6), stage(4), clkGen(), stage(3), stage012(), stage(5)] - + def testZeroDelay(self): """ Zero delay behavior """ bench = self.bench() Simulation(bench).run(quiet=QUIET) - def initSignal(waveform): interval, val, sigdelay = waveform[0] if sigdelay: @@ -623,18 +625,23 @@ def initSignal(waveform): else: return Signal(val=val) + def isPosedge(oldval, val): return not oldval and val + def isNegedge(oldval, val): return oldval and not val + def isEvent(oldval, val): return oldval != val + def isEdge(oldval, val): return isPosedge(oldval, val) or isNegedge(oldval, val) + def getExpectedTimes(waveform, eventCheck): interval, val, sigdelay = waveform[0] # print waveform[0] @@ -653,16 +660,16 @@ def getExpectedTimes(waveform, eventCheck): inctime += waveform[j][0] newval = waveform[j][1] newsigdelay = waveform[j][2] - if newval != val: # cancel event + if newval != val: # cancel event break - else: # same vals + else: # same vals if inctime + newsigdelay < sigdelay: # special case: there is a later event, with same val, # but smaller delay: presumably, this should win, # so cancel the present one break j += 1 - else: # if event was not cancelled by a break + else: # if event was not cancelled by a break if eventCheck(oldval, val): expected.append(time + sigdelay) # print expected[-1] @@ -673,7 +680,7 @@ def getExpectedTimes(waveform, eventCheck): class Waveform(TestCase): - + """ Test of all sorts of event response in a waveform """ waveform = [] @@ -684,7 +691,7 @@ class Waveform(TestCase): val = randrange(0, 4) waveform.append((interval, val, sigdelay)) duration = interval + duration - + def stimulus(self): for interval, val, sigdelay in self.waveform: yield delay(interval) @@ -693,36 +700,36 @@ class Waveform(TestCase): self.sig.delay = sigdelay def response(self, clause, expected): - self.assertTrue(len(expected) > 100) # we should test something + assert len(expected) > 100 # we should test something i = 0 while 1: yield clause - self.assertEqual(now(), expected[i]) + assert now() == expected[i] i += 1 - + def setUp(self): self.sig = initSignal(self.waveform) def runSim(self, sim): sim.run(quiet=QUIET) - + def testPosedge(self): """ Posedge waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isPosedge) + expected = getExpectedTimes(self.waveform, isPosedge) response = self.response(clause=s.posedge, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) + assert self.duration <= now() def testNegedge(self): """ Negedge waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isNegedge) + expected = getExpectedTimes(self.waveform, isNegedge) response = self.response(clause=s.negedge, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) + assert self.duration <= now() def testEdge(self): """ Edge waveform test """ @@ -732,58 +739,58 @@ class Waveform(TestCase): response = self.response(clause=(s.negedge, s.posedge), expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) + assert self.duration <= now() def testEvent(self): """ Event waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isEvent) + expected = getExpectedTimes(self.waveform, isEvent) # print expected response = self.response(clause=s, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) - + assert self.duration <= now() + def testRedundantEvents(self): """ Redundant event waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isEvent) + expected = getExpectedTimes(self.waveform, isEvent) response = self.response(clause=(s,) * 6, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) - - def testRedundantEventAndEdges(self): + assert self.duration <= now() + + def testRedundantEventAndEdges(self): """ Redundant edge waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isEvent) + expected = getExpectedTimes(self.waveform, isEvent) response = self.response(clause=(s, s.negedge, s.posedge), expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) - + assert self.duration <= now() + def testRedundantPosedges(self): """ Redundant posedge waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isPosedge) + expected = getExpectedTimes(self.waveform, isPosedge) response = self.response(clause=(s.posedge,) * 3, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) + assert self.duration <= now() def testRedundantNegedges(self): """ Redundant negedge waveform test """ s = self.sig stimulus = self.stimulus() - expected = getExpectedTimes(self.waveform, isNegedge) + expected = getExpectedTimes(self.waveform, isNegedge) response = self.response(clause=(s.negedge,) * 9, expected=expected) self.runSim(Simulation(stimulus, response)) - self.assertTrue(self.duration <= now()) + assert self.duration <= now() + - class WaveformSigDelay(Waveform): - + """ Repeat waveform tests with a delayed signal """ waveform = [] @@ -795,10 +802,10 @@ class WaveformSigDelay(Waveform): sigdelay = randrange(1, 20) waveform.append((interval, val, sigdelay)) duration += interval - + class WaveformInertialDelay(Waveform): - + """ Repeat waveform tests to check inertial delay """ waveform = [] @@ -811,8 +818,9 @@ class WaveformInertialDelay(Waveform): waveform.append((interval, val, sigdelay)) duration += interval + class WaveformInertialDelayStress(Waveform): - + """ Repeat waveform tests to stress inertial delay """ waveform = [] @@ -825,29 +833,32 @@ class WaveformInertialDelayStress(Waveform): waveform.append((interval, val, sigdelay)) duration += interval + class SimulationRunMethod(Waveform): - + """ Basic test of run method of Simulation object """ - + def runSim(self, sim): duration = randrange(1, 300) while sim.run(duration, quiet=QUIET): duration = randrange(1, 300) - + class TimeZeroEvents(TestCase): """ Check events at time 0 """ def bench(self, sig, next, clause, timeout=1): val = sig.val + def stimulus(): sig.next = next yield delay(10) + def response(): yield clause, delay(timeout) - self.assertEqual(now(), 0) - self.assertEqual(sig.val, next) + assert now() == 0 + assert sig.val == next return [stimulus(), response()] def testEvent(self): @@ -861,15 +872,9 @@ class TimeZeroEvents(TestCase): s = Signal(0) testBench = self.bench(sig=s, next=1, clause=s.posedge) Simulation(testBench).run(quiet=QUIET) - + def testNegedge(self): """ Negedge at time 0 """ s = Signal(1) testBench = self.bench(sig=s, next=0, clause=s.negedge) Simulation(testBench).run(quiet=QUIET) - - - -if __name__ == "__main__": - unittest.main() - diff --git a/myhdl/test/core/test_always.py b/myhdl/test/core/test_always.py index 0269aae2..505b3f88 100644 --- a/myhdl/test/core/test_always.py +++ b/myhdl/test/core/test_always.py @@ -20,84 +20,57 @@ """ Run the unit tests for the @always decorator """ from __future__ import absolute_import - -import random from random import randrange + +from myhdl import (AlwaysError, Signal, Simulation, StopSimulation, delay, + instances, intbv, now) +from myhdl._always import _error, always +from myhdl._Waiter import (_DelayWaiter, _EdgeTupleWaiter, _EdgeWaiter, + _SignalTupleWaiter, _SignalWaiter, _Waiter) +from helpers import raises_kind + # random.seed(3) # random, but deterministic -import unittest -from unittest import TestCase -import inspect - -from myhdl import Signal, Simulation, instances, AlwaysError, \ - intbv, delay, StopSimulation, now - -from myhdl._always import always, _Always, _error - -from myhdl._Waiter import _inferWaiter, _Waiter -from myhdl._Waiter import _SignalWaiter,_SignalTupleWaiter, _DelayWaiter, \ - _EdgeWaiter, _EdgeTupleWaiter - QUIET=1 + def g(): pass x = Signal(0) -class AlwaysCompilationTest(TestCase): - + +class TestAlwaysCompilation: def testArgIsFunction(self): h = 5 - try: + with raises_kind(AlwaysError, _error.ArgType): always(delay(3))(h) - except AlwaysError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() - + def testArgIsNormalFunction(self): - try: + with raises_kind(AlwaysError, _error.ArgType): @always(delay(3)) def h(): yield None - except AlwaysError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() def testArgHasNoArgs(self): - try: + with raises_kind(AlwaysError, _error.NrOfArgs): @always(delay(3)) def h(n): return n - except AlwaysError as e: - self.assertEqual(e.kind, _error.NrOfArgs) - else: - self.fail() def testDecArgType1(self): - try: + with raises_kind(AlwaysError, _error.DecArgType): @always def h(n): return n - except AlwaysError as e: - self.assertEqual(e.kind, _error.DecArgType) - else: - self.fail() def testDecArgType2(self): - try: + with raises_kind(AlwaysError, _error.DecArgType): @always(g) def h(n): return n - except AlwaysError as e: - self.assertEqual(e.kind, _error.DecArgType) - else: - self.fail() - def SignalFunc1(a, b, c, d, r): @@ -146,24 +119,23 @@ def EdgeTupleFunc1(a, b, c, d, r): def GeneralFunc(a, b, c, d, r): - + @always(c.posedge, d) def logic(): r.next = a + b + c + d return logic - -class InferWaiterTest(TestCase): +class TestInferWaiter: def bench(self, MyHDLFunc, waiterType): a, b, c, d, r, s = [Signal(intbv(0)) for i in range(6)] inst_r = MyHDLFunc(a, b, c, d, r) - self.assertEqual(type(inst_r.waiter), waiterType) - + assert type(inst_r.waiter) == waiterType + inst_s = MyHDLFunc(a, b, c, d, s) def stimulus(): @@ -180,14 +152,14 @@ class InferWaiterTest(TestCase): def check(): while 1: yield a, b, c, r, s - self.assertEqual(r, s) + assert r == s return inst_r, _Waiter(inst_s.gen), _Waiter(stimulus()), _Waiter(check()) def testSignal1(self): sim = Simulation(self.bench(SignalFunc1, _SignalWaiter)) sim.run() - + def testSignalTuple1(self): sim = Simulation(self.bench(SignalTupleFunc1, _SignalTupleWaiter)) sim.run() @@ -199,17 +171,11 @@ class InferWaiterTest(TestCase): def testEdge1(self): sim = Simulation(self.bench(EdgeFunc1, _EdgeWaiter)) sim.run() - + def testEdgeTuple1(self): sim = Simulation(self.bench(EdgeTupleFunc1, _EdgeTupleWaiter)) sim.run() - + def testGeneral(self): sim = Simulation(self.bench(GeneralFunc, _Waiter)) sim.run() - - - -if __name__ == "__main__": - unittest.main() - diff --git a/myhdl/test/core/test_always_comb.py b/myhdl/test/core/test_always_comb.py index 85e0d150..c05cb547 100644 --- a/myhdl/test/core/test_always_comb.py +++ b/myhdl/test/core/test_always_comb.py @@ -20,61 +20,45 @@ """ Run the unit tests for always_comb """ from __future__ import absolute_import - import random from random import randrange + +from myhdl import (AlwaysCombError, Signal, Simulation, StopSimulation, delay, + instances, intbv, now) +from myhdl._always_comb import _error, always_comb +from myhdl._Waiter import _SignalTupleWaiter, _SignalWaiter, _Waiter +from helpers import raises_kind + # random.seed(3) # random, but deterministic -import unittest -from unittest import TestCase -import inspect - -from myhdl import Signal, Simulation, instances, AlwaysCombError, \ - intbv, delay, StopSimulation, now - -from myhdl._always_comb import always_comb, _AlwaysComb, _error - -from myhdl._Waiter import _Waiter,_SignalWaiter,_SignalTupleWaiter - QUIET=1 + def g(): pass x = Signal(0) -class AlwaysCombCompilationTest(TestCase): - + +class TestAlwaysCombCompilation: def testArgIsFunction(self): h = 5 - try: + with raises_kind(AlwaysCombError, _error.ArgType): always_comb(h) - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() - + def testArgIsNormalFunction(self): def h(): yield None - try: + with raises_kind(AlwaysCombError, _error.ArgType): always_comb(h) - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() def testArgHasNoArgs(self): def h(n): return n - try: + with raises_kind(AlwaysCombError, _error.NrOfArgs): always_comb(h) - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.NrOfArgs) - else: - self.fail() ## def testScope(self): ## try: @@ -87,130 +71,129 @@ class AlwaysCombCompilationTest(TestCase): def testInfer1(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): c.next = a v = u g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a']) - self.assertEqual(i.inputs, expected) - + assert i.inputs == expected + def testInfer2(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): c.next = x g = a g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a', 'x']) - self.assertEqual(i.inputs, expected) + assert i.inputs == expected def testInfer3(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): c.next = a + x + u a = 1 g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['x']) - self.assertEqual(i.inputs, expected) + assert i.inputs == expected def testInfer4(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): c.next = a + x + u x = 1 g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a']) - self.assertEqual(i.inputs, expected) - - + assert i.inputs == expected + def testInfer5(self): a, b, c, d = [Signal(0) for i in range(4)] + def h(): c.next += 1 a += 1 - try: + with raises_kind(AlwaysCombError, _error.SignalAsInout % set('c')): g = always_comb(h).gen - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.SignalAsInout % "c") - else: - self.fail() def testInfer6(self): a, b, c, d = [Signal(0) for i in range(4)] + def h(): c.next = a x.next = c - try: + with raises_kind(AlwaysCombError, _error.SignalAsInout % set('c')): g = always_comb(h).gen - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.SignalAsInout % "c") - else: - self.fail() def testInfer7(self): a, b, c, d = [Signal(0) for i in range(4)] + def h(): c.next[a:0] = x[b:0] g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a', 'b', 'x']) - self.assertEqual(i.inputs, expected) - + assert i.inputs == expected + def testInfer8(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): v = 2 c.next[8:1+a+v] = x[4:b*3+u] g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a', 'b', 'x']) - self.assertEqual(i.inputs, expected) - + assert i.inputs == expected + def testInfer9(self): a, b, c, d = [Signal(0) for i in range(4)] + def h(): c.next[a-1] = x[b-1] g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a', 'b', 'x']) - self.assertEqual(i.inputs, expected) - + assert i.inputs == expected + def testInfer10(self): a, b, c, d = [Signal(0) for i in range(4)] + def f(x, y, z): return 0 + def h(): c.next = f(a, 2*b, d*x) g = always_comb(h).gen i = g.gi_frame.f_locals['self'] expected = set(['a', 'b', 'd', 'x']) - self.assertEqual(i.inputs, expected) + assert i.inputs == expected def testEmbeddedFunction(self): a, b, c, d = [Signal(0) for i in range(4)] u = 1 + def h(): def g(): e = b return e c.next = x g = a - try: + with raises_kind(AlwaysCombError, _error.EmbeddedFunction): g = always_comb(h) - except AlwaysCombError as e: - self.assertEqual(e.kind, _error.EmbeddedFunction) - else: - self.fail() -class AlwaysCombSimulationTest1(TestCase): +class TestAlwaysCombSimulation1: def bench(self, function): @@ -223,7 +206,6 @@ class AlwaysCombSimulationTest1(TestCase): vectors = [intbv(j) for i in range(32) for j in range(16)] random.shuffle(vectors) - def combFunc(): if __debug__: f = x @@ -249,22 +231,21 @@ class AlwaysCombSimulationTest1(TestCase): d.next = v[3] yield clk.posedge yield clk.negedge - self.assertEqual(x, z) + assert x == z raise StopSimulation("always_comb simulation test") return instances() - def testAnd(self): def andFunction(a, b, c, d): return a & b & c & d Simulation(self.bench(andFunction)).run(quiet=QUIET) - + def testOr(self): def orFunction(a, b, c, d): return a | b | c | d Simulation(self.bench(orFunction)).run(quiet=QUIET) - + def testXor(self): def xorFunction(a, b, c, d): return a ^ b ^ c ^ d @@ -283,8 +264,8 @@ class AlwaysCombSimulationTest1(TestCase): return not (a & (not b)) | ((not c) & d) Simulation(self.bench(function)).run(quiet=QUIET) - -class AlwaysCombSimulationTest2(TestCase): + +class TestAlwaysCombSimulation2: def bench(self, funcName="and"): @@ -301,20 +282,23 @@ class AlwaysCombSimulationTest2(TestCase): def andFunc(): x.next = a & b & c & d + def andGenFunc(): while 1: z.next = a & b & c & d yield a, b, c, d - + def orFunc(): x.next = a | b | c | d + def orGenFunc(): while 1: z.next = a | b | c | d yield a, b, c, d - + def logicFunc(): x.next = not (a & (not b)) | ((not c) & d) + def logicGenFunc(): while 1: z.next = not (a & (not b)) | ((not c) & d) @@ -322,11 +306,12 @@ class AlwaysCombSimulationTest2(TestCase): def incFunc(): x.next = k + 1 + def incGenFunc(): while 1: z.next = k + 1 yield k - + combFunc = eval(funcName + "Func") comb = always_comb(combFunc) genFunc = eval(funcName + "GenFunc") @@ -346,29 +331,26 @@ class AlwaysCombSimulationTest2(TestCase): k.next = v yield clk.posedge yield clk.negedge - self.assertEqual(x, z) + assert x == z raise StopSimulation("always_comb simulation test") return comb, gen, clkGen(), stimulus() - def testAnd(self): Simulation(self.bench("and")).run(quiet=QUIET) - + def testOr(self): Simulation(self.bench("or")).run(quiet=QUIET) - + def testLogic(self): Simulation(self.bench("logic")).run(quiet=QUIET) - + def testInc(self): Simulation(self.bench("inc")).run(quiet=QUIET) - - def SignalGen1(a, b, c, d, r): - + @always_comb def logic(): r.next = a @@ -385,16 +367,15 @@ def SignalTupleGen1(a, b, c, d, r): return logic - -class InferWaiterTest(TestCase): +class TestInferWaiter: def bench(self, MyHDLFunc, waiterType): a, b, c, d, r, s = [Signal(intbv(0)) for i in range(6)] inst_r = MyHDLFunc(a, b, c, d, r) - self.assertEqual(type(inst_r.waiter), waiterType) - + assert type(inst_r.waiter) == waiterType + inst_s = MyHDLFunc(a, b, c, d, s) def stimulus(): @@ -411,19 +392,14 @@ class InferWaiterTest(TestCase): def check(): while 1: yield a, b, c, r, s - self.assertEqual(r, s) + assert r == s return inst_r, _Waiter(inst_s.gen), _Waiter(stimulus()), _Waiter(check()) def testSignal1(self): sim = Simulation(self.bench(SignalGen1, _SignalWaiter)) sim.run() - + def testSignalTuple1(self): sim = Simulation(self.bench(SignalTupleGen1, _SignalTupleWaiter)) sim.run() - - - -if __name__ == "__main__": - unittest.main() diff --git a/myhdl/test/core/test_always_seq.py b/myhdl/test/core/test_always_seq.py index 1506ac0c..ffafbd91 100644 --- a/myhdl/test/core/test_always_seq.py +++ b/myhdl/test/core/test_always_seq.py @@ -1,11 +1,7 @@ -from random import randrange -from pytest import raises from myhdl import * - -from myhdl import Signal, Simulation, instances, now - -from myhdl._always_seq import always_seq, _AlwaysSeq, _error, AlwaysSeqError - +from myhdl import Signal +from myhdl._always_seq import AlwaysSeqError, _error, always_seq +from helpers import raises_kind def test_clock(): @@ -15,11 +11,10 @@ def test_clock(): clock = Signal(bool(0)) reset = ResetSignal(0, active=0, async=True) - with raises(AlwaysSeqError) as e: + with raises_kind(AlwaysSeqError, _error.EdgeType): @always_seq(clock, reset=reset) def logic1(): pass - assert e.kind == _error.EdgeType # should work with a valid Signal clock = Signal(bool(0)) @@ -30,18 +25,18 @@ def test_clock(): except: assert False -def test_reset(): + +def test_reset(): """ check the reset parameter """ - + # should fail without a valid ResetSignal clock = Signal(bool(0)) reset = Signal(bool(0)) - with raises(AlwaysSeqError) as e: + with raises_kind(AlwaysSeqError, _error.ResetType): @always_seq(clock.posedge, reset=reset) def logic(): pass - assert e.kind == _error.ResetType # should work with a valid Signal reset = ResetSignal(0, active=0, async=True) @@ -51,4 +46,3 @@ def test_reset(): pass except: assert False - diff --git a/myhdl/test/core/test_bin.py b/myhdl/test/core/test_bin.py index ab0b69d0..1db13ae4 100644 --- a/myhdl/test/core/test_bin.py +++ b/myhdl/test/core/test_bin.py @@ -20,20 +20,19 @@ """ Run the unit tests for bin """ from __future__ import absolute_import - import random -from random import randrange -random.seed(1) # random, but deterministic - -import unittest -from unittest import TestCase import sys +from random import randrange from myhdl import bin from myhdl._compat import long +random.seed(1) # random, but deterministic + + SIZE = 100 + def _int2bitstring(num): if num == 0: return '0' @@ -41,6 +40,7 @@ def _int2bitstring(num): return '1' return _int2bitstring(num // 2) + _int2bitstring(num % 2) + def binref(num, width=0): """Return a binary string representation. @@ -58,51 +58,41 @@ def binref(num, width=0): return s -class TestBin(TestCase): +class TestBin: def testSmall(self): for i in range(-65, 65): - self.assertEqual(bin(i), binref(i)) - + assert bin(i) == binref(i) + def testSmallWidth(self): for i in range(-65, 65): w = randrange(1, 8) - self.assertEqual(bin(i, w), binref(i, w)) + assert bin(i, w) == binref(i, w) def testRandomInt(self): for j in range(SIZE): i = randrange(-sys.maxsize, sys.maxsize) - self.assertEqual(bin(i), binref(i)) - + assert bin(i) == binref(i) + def testRandomIntWidth(self): for j in range(SIZE): w = randrange(1, 1000) i = randrange(-sys.maxsize, sys.maxsize) - self.assertEqual(bin(i, w), binref(i, w)) + assert bin(i, w) == binref(i, w) def testRandomLong(self): for j in range(SIZE): k = randrange(sys.maxsize) i = k + sys.maxsize - self.assertEqual(bin(i), binref(i)) + assert bin(i) == binref(i) i = -k - sys.maxsize - self.assertEqual(bin(i), binref(i)) - + assert bin(i) == binref(i) + def testRandomLongWith(self): for j in range(SIZE): w = randrange(1, 1000) k = randrange(sys.maxsize) i = k + sys.maxsize - self.assertEqual(bin(i, w), binref(i, w)) + assert bin(i, w) == binref(i, w) i = -k - sys.maxsize - self.assertEqual(bin(i, w), binref(i, w)) - - - - - - - - -if __name__ == "__main__": - unittest.main() + assert bin(i, w) == binref(i, w) diff --git a/myhdl/test/core/test_concat.py b/myhdl/test/core/test_concat.py index 8f10a9c5..31e4cd2b 100644 --- a/myhdl/test/core/test_concat.py +++ b/myhdl/test/core/test_concat.py @@ -20,23 +20,21 @@ """ Run the concatunit tests. """ from __future__ import absolute_import - -import unittest -from unittest import TestCase -import random -from random import randrange -from functools import reduce -random.seed(2) # random, but deterministic import operator +import random +from functools import reduce +import pytest + +from myhdl._compat import long +from myhdl._concat import concat from myhdl._intbv import intbv from myhdl._Signal import Signal -from myhdl._concat import concat -from myhdl._compat import long + +random.seed(2) # random, but deterministic - -class TestConcat(TestCase): +class TestConcat: bases = ("0", "1", "10101", "01010", "110", "011", "1001000100001011111000") extslist = [ ["0"], ["1"], ["00"], ["11"], ["000"], ["111"], ["1010101010"], @@ -52,8 +50,8 @@ class TestConcat(TestCase): refstr = basestr + reduce(operator.add, extstr) reflen = len(refstr) ref = long(refstr, 2) - self.assertEqual(bv, ref) - self.assertEqual(len(bv), reflen) + assert bv == ref + assert len(bv) == reflen def ConcatToUnsizedBase(self, bases, extslist): for base, basestr in zip(bases, self.bases): @@ -61,9 +59,8 @@ class TestConcat(TestCase): bv = concat(base, *exts) refstr = basestr + reduce(operator.add, extstr) ref = long(refstr, 2) - self.assertEqual(bv, ref) - self.assertEqual(len(bv), 0) - + assert bv == ref + assert len(bv) == 0 def testConcatStringsToString(self): bases = self.bases @@ -74,12 +71,12 @@ class TestConcat(TestCase): bases = [long(base, 2) for base in self.bases] extslist = self.extslist self.ConcatToUnsizedBase(bases, extslist) - + def testConcatStringsToSignalInt(self): bases = [Signal(long(base, 2)) for base in self.bases] extslist = self.extslist self.ConcatToUnsizedBase(bases, extslist) - + def testConcatStringsToIntbv(self): bases = [intbv(base) for base in self.bases] extslist = self.extslist @@ -101,7 +98,7 @@ class TestConcat(TestCase): bases.append(intbv(base)) extslist = self.extslist self.ConcatToSizedBase(bases, extslist) - + def testConcatStringsToSignalBool(self): if type(bool) is not type: return @@ -113,15 +110,14 @@ class TestConcat(TestCase): bases.append(intbv(base)) extslist = self.extslist self.ConcatToSizedBase(bases, extslist) - - + def testConcatIntbvsToIntbv(self): bases = [intbv(base) for base in self.bases] extslist = [] for exts in self.extslist: extslist.append([intbv(ext) for ext in exts]) self.ConcatToSizedBase(bases, extslist) - + def testConcatSignalIntbvsToIntbv(self): bases = [intbv(base) for base in self.bases] extslist = [] @@ -143,14 +139,13 @@ class TestConcat(TestCase): extslist.append([Signal(intbv(ext)) for ext in exts]) self.ConcatToSizedBase(bases, extslist) - def testConcatIntbvsToInt(self): bases = [long(base, 2) for base in self.bases] extslist = [] for exts in self.extslist: extslist.append([intbv(ext) for ext in exts]) self.ConcatToUnsizedBase(bases, extslist) - + def testConcatSignalIntbvsToInt(self): bases = [long(base, 2) for base in self.bases] extslist = [] @@ -171,7 +166,6 @@ class TestConcat(TestCase): for exts in self.extslist: extslist.append([Signal(intbv(ext)) for ext in exts]) self.ConcatToUnsizedBase(bases, extslist) - def testConcatIntbvsBoolsToIntbv(self): if type(bool) is not type: @@ -187,7 +181,6 @@ class TestConcat(TestCase): newexts.append(intbv(ext)) extslist.append(newexts) self.ConcatToSizedBase(bases, extslist) - def testConcatMixToSizedBase(self): bases = [] @@ -216,7 +209,6 @@ class TestConcat(TestCase): newexts.append(random.choice(seq)) extslist.append(newexts) self.ConcatToUnsizedBase(bases, extslist) - def testConcatMixBoolToSizedBase(self): if type(bool) is not type: @@ -236,18 +228,14 @@ class TestConcat(TestCase): newexts.append(random.choice(seq)) extslist.append(newexts) self.ConcatToSizedBase(bases, extslist) - def testWrongType(self): a = intbv(4) - self.assertRaises(TypeError, concat, a, 5) - + with pytest.raises(TypeError): + concat(a, 5) + def testUnsizedConcat(self): a = intbv(4) b = intbv(5) - self.assertRaises(TypeError, concat, a, b) - -if __name__ == "__main__": - unittest.main() - - + with pytest.raises(TypeError): + concat(a, b) diff --git a/myhdl/test/core/test_enum.py b/myhdl/test/core/test_enum.py index f0b78e48..243809d3 100644 --- a/myhdl/test/core/test_enum.py +++ b/myhdl/test/core/test_enum.py @@ -20,69 +20,45 @@ """ Run the unit tests for enum """ from __future__ import absolute_import - -import random -from random import randrange -random.seed(1) # random, but deterministic - -import sys import copy +import random -import unittest -from unittest import TestCase +import pytest from myhdl import enum +random.seed(1) # random, but deterministic + t_State = enum("SEARCH", "CONFIRM", "SYNC") t_Homograph = enum("SEARCH", "CONFIRM", "SYNC") -class TestEnum(TestCase): +class TestEnum: def testUniqueLiterals(self): - try: + with pytest.raises(ValueError): t_State = enum("SEARCH", "CONFIRM", "SEARCH") - except ValueError: - pass - else: - self.fail() def testWrongAttr(self): - try: + with pytest.raises(AttributeError): t_State.TYPO - except AttributeError: - pass - else: - self.fail() def testAttrAssign(self): - try: + with pytest.raises(AttributeError): t_State.SEARCH = 4 - except AttributeError: - pass - else: - self.fail() def testWrongAttrAssign(self): - try: + with pytest.raises(AttributeError): t_State.TYPO = 4 - except AttributeError: - pass - else: - self.fail() def testHomograph(self): - self.assertTrue(t_State is not t_Homograph) - + assert t_State is not t_Homograph + def testHomographLiteral(self): - self.assertTrue(t_State.SEARCH is not t_Homograph.SEARCH) + assert t_State.SEARCH is not t_Homograph.SEARCH def testItemCopy(self): e = copy.deepcopy(t_State.SEARCH) - self.assertTrue(e == t_State.SEARCH) - self.assertTrue(e != t_State.CONFIRM) - - -if __name__ == "__main__": - unittest.main() + assert e == t_State.SEARCH + assert e != t_State.CONFIRM diff --git a/myhdl/test/core/test_inferWaiter.py b/myhdl/test/core/test_inferWaiter.py index df0d19b2..e97a0cdf 100644 --- a/myhdl/test/core/test_inferWaiter.py +++ b/myhdl/test/core/test_inferWaiter.py @@ -22,20 +22,19 @@ from __future__ import absolute_import import random from random import randrange -random.seed(1) # random, but deterministic from types import GeneratorType -import unittest -from unittest import TestCase - from myhdl import * -from myhdl._Waiter import _inferWaiter, _Waiter -from myhdl._Waiter import _SignalWaiter,_SignalTupleWaiter, _DelayWaiter, \ - _EdgeWaiter, _EdgeTupleWaiter +from myhdl._Waiter import (_DelayWaiter, _EdgeTupleWaiter, _EdgeWaiter, + _inferWaiter, _SignalTupleWaiter, _SignalWaiter, + _Waiter) + +random.seed(1) # random, but deterministic QUIET=1 + def SignalFunc1(a, b, c, d, r): @instance def logic(): @@ -52,6 +51,7 @@ def SignalFunc2(a, b, c, d, r): r.next = a - b + c return logic(a, r) + def SignalTupleFunc1(a, b, c, d, r): @instance def logic(): @@ -60,6 +60,7 @@ def SignalTupleFunc1(a, b, c, d, r): r.next = a + b + c return logic + def SignalTupleFunc2(a, b, c, d, r): def logic(a, r): while 1: @@ -67,6 +68,7 @@ def SignalTupleFunc2(a, b, c, d, r): r.next = a - b + c return logic(a, r) + def DelayFunc(a, b, c, d, r): @instance def logic(): @@ -75,6 +77,7 @@ def DelayFunc(a, b, c, d, r): r.next = a + b + c return logic + def EdgeFunc1(a, b, c, d, r): @instance def logic(): @@ -83,6 +86,7 @@ def EdgeFunc1(a, b, c, d, r): r.next = a + b + c return logic + def EdgeFunc2(a, b, c, d, r): def logic(c, r): while 1: @@ -95,6 +99,7 @@ def EdgeFunc2(a, b, c, d, r): r.next = a + b - c return logic(c, r) + def EdgeTupleFunc1(a, b, c, d, r): @instance def logic(): @@ -103,6 +108,7 @@ def EdgeTupleFunc1(a, b, c, d, r): r.next = a + b + c return logic + def EdgeTupleFunc2(a, b, c, d, r): def logic(c, r): while 1: @@ -114,7 +120,8 @@ def EdgeTupleFunc2(a, b, c, d, r): else: r.next = a + b - c return logic(c, r) - + + def GeneralFunc(a, b, c, d, r): def logic(c, r): while 1: @@ -126,22 +133,21 @@ def GeneralFunc(a, b, c, d, r): else: r.next = a + b - c return logic(c, r) - -class InferWaiterTest(TestCase): +class TestInferWaiter: def bench(self, genFunc, waiterType): a, b, c, d, r, s = [Signal(intbv(0)) for i in range(6)] gen_inst_r = genFunc(a, b, c, d, r) - if not isinstance(gen_inst_r, GeneratorType): # decorator type + if not isinstance(gen_inst_r, GeneratorType): # decorator type gen_inst_r = gen_inst_r.gen - self.assertEqual(type(_inferWaiter(gen_inst_r)), waiterType) - + assert type(_inferWaiter(gen_inst_r)) == waiterType + gen_inst_s = genFunc(a, b, c, d, s) - if not isinstance(gen_inst_s, GeneratorType): # decorator type + if not isinstance(gen_inst_s, GeneratorType): # decorator type gen_inst_s = gen_inst_s.gen def stimulus(): @@ -158,18 +164,18 @@ class InferWaiterTest(TestCase): def check(): while 1: yield a, b, c, r, s - self.assertEqual(r, s) + assert r == s return gen_inst_r, _Waiter(gen_inst_s), _Waiter(stimulus()), _Waiter(check()) def testSignal1(self): sim = Simulation(self.bench(SignalFunc1, _SignalWaiter)) sim.run() - + def testSignal2(self): sim = Simulation(self.bench(SignalFunc2, _SignalWaiter)) sim.run() - + def testSignalTuple1(self): sim = Simulation(self.bench(SignalTupleFunc1, _SignalTupleWaiter)) sim.run() @@ -185,7 +191,7 @@ class InferWaiterTest(TestCase): def testEdge1(self): sim = Simulation(self.bench(EdgeFunc1, _EdgeWaiter)) sim.run() - + def testEdge2(self): sim = Simulation(self.bench(EdgeFunc2, _EdgeWaiter)) sim.run() @@ -201,7 +207,3 @@ class InferWaiterTest(TestCase): def testGeneral(self): sim = Simulation(self.bench(GeneralFunc, _Waiter)) sim.run() - - -if __name__ == "__main__": - unittest.main() diff --git a/myhdl/test/core/test_instance.py b/myhdl/test/core/test_instance.py index ea977f66..5f8d2441 100644 --- a/myhdl/test/core/test_instance.py +++ b/myhdl/test/core/test_instance.py @@ -20,61 +20,38 @@ """ Run the unit tests for instance """ from __future__ import absolute_import +from myhdl import (InstanceError, Signal, Simulation, StopSimulation, delay, + instances, intbv, now) +from myhdl._instance import _error, instance +from helpers import raises_kind -import random -from random import randrange # random.seed(3) # random, but deterministic -import unittest -from unittest import TestCase -import inspect - -from myhdl import Signal, Simulation, instances, InstanceError, \ - intbv, delay, StopSimulation, now - -from myhdl._instance import instance, _error - - QUIET=1 + def g(): pass x = Signal(0) -class InstanceCompilationTest(TestCase): - + +class TestInstanceCompilation: def testArgIsFunction(self): h = 5 - try: + with raises_kind(InstanceError, _error.ArgType): instance(h) - except InstanceError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() - + def testArgIsGeneratorFunction(self): - try: + with raises_kind(InstanceError, _error.ArgType): @instance def h(): return None - except InstanceError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() def testArgHasNoArgs(self): - try: + with raises_kind(InstanceError, _error.NrOfArgs): @instance def h(n): yield n - except InstanceError as e: - self.assertEqual(e.kind, _error.NrOfArgs) - else: - self.fail() - - -if __name__ == "__main__": - unittest.main() diff --git a/myhdl/test/core/test_intbv.py b/myhdl/test/core/test_intbv.py index fb7ba864..e771acea 100644 --- a/myhdl/test/core/test_intbv.py +++ b/myhdl/test/core/test_intbv.py @@ -20,25 +20,25 @@ """ Run the intbv unit tests. """ from __future__ import absolute_import - -import unittest -from unittest import TestCase -import random -from random import randrange -random.seed(2) # random, but deterministic -import sys -maxint = sys.maxsize import operator +import random +import sys from copy import copy, deepcopy +from random import randrange +import pytest + +from myhdl._compat import integer_types, long from myhdl._intbv import intbv -from myhdl._compat import long, integer_types +random.seed(2) # random, but deterministic +maxint = sys.maxsize -class TestIntbvInit(TestCase): + +class TestIntbvInit: def testDefaultValue(self): - self.assertEqual(intbv(), 0) - + assert intbv() == 0 + def getItem(s, i): ext = '0' * (i-len(s)+1) @@ -46,6 +46,7 @@ def getItem(s, i): si = len(exts)-1-i return exts[si] + def getSlice(s, i, j): ext = '0' * (i-len(s)+1) exts = ext + s @@ -53,6 +54,7 @@ def getSlice(s, i, j): sj = len(exts)-j return exts[si:sj] + def getSliceLeftOpen(s, j): ext = '0' * (j-len(s)+1) exts = ext + s @@ -61,12 +63,14 @@ def getSliceLeftOpen(s, j): else: return exts + def setItem(s, i, val): ext = '0' * (i-len(s)+1) exts = ext + s si = len(exts)-1-i return exts[:si] + val + exts[si+1:] + def setSlice(s, i, j, val): ext = '0' * (i-len(s)+1) exts = ext + s @@ -74,6 +78,7 @@ def setSlice(s, i, j, val): sj = len(exts)-j return exts[:si] + val[si-sj:] + exts[sj:] + def setSliceLeftOpen(s, j, val): ext = '0' * (j-len(s)+1) exts = ext + s @@ -81,9 +86,9 @@ def setSliceLeftOpen(s, j, val): return val + exts[-j:] else: return val - -class TestIntBvIndexing(TestCase): + +class TestIntBvIndexing: def seqsSetup(self): seqs = ["0", "1", "000", "111", "010001", "110010010", "011010001110010"] @@ -107,10 +112,10 @@ class TestIntBvIndexing(TestCase): ref = long(getItem(s, i), 2) res = bv[i] resi = bvi[i] - self.assertEqual(res, ref) - self.assertEqual(type(res), bool) - self.assertEqual(resi, ref^1) - self.assertEqual(type(resi), bool) + assert res == ref + assert type(res) == bool + assert resi == ref^1 + assert type(resi) == bool def testGetSlice(self): self.seqsSetup() @@ -124,15 +129,15 @@ class TestIntBvIndexing(TestCase): res = bv[i:j] resi = bvi[i:j] except ValueError: - self.assertTrue(i<=j) + assert i<=j continue ref = long(getSlice(s, i, j), 2) - self.assertEqual(res, ref) - self.assertEqual(type(res), intbv) + assert res == ref + assert type(res) == intbv mask = (2**(i-j))-1 - self.assertEqual(resi, ref ^ mask) - self.assertEqual(type(resi), intbv) - + assert resi == ref ^ mask + assert type(resi) == intbv + def testGetSliceLeftOpen(self): self.seqsSetup() for s in self.seqs: @@ -143,11 +148,10 @@ class TestIntBvIndexing(TestCase): res = bv[:j] resi = bvi[:j] ref = long(getSliceLeftOpen(s, j), 2) - self.assertEqual(res, ref) - self.assertEqual(type(res), intbv) - self.assertEqual(resi+ref, -1) - self.assertEqual(type(res), intbv) - + assert res == ref + assert type(res) == intbv + assert resi+ref == -1 + assert type(res) == intbv def testSetItem(self): self.seqsSetup() @@ -168,14 +172,14 @@ class TestIntBvIndexing(TestCase): ref1 = long(setItem(s, i, '1'), 2) ref0i = ~long(setItem(s, i, '1'), 2) ref1i = ~long(setItem(s, i, '0'), 2) - self.assertEqual(bv0, ref0) - self.assertEqual(bv1, ref1) - self.assertEqual(bv0i, ref0i) - self.assertEqual(bv1i, ref1i) - + assert bv0 == ref0 + assert bv1 == ref1 + assert bv0i == ref0i + assert bv1i == ref1i + def testSetSlice(self): self.seqsSetup() - toggle = 0 + toggle = 0 for s in self.seqs: n = long(s, 2) for i in range(1, len(s)+5): @@ -192,25 +196,25 @@ class TestIntBvIndexing(TestCase): try: bv[i:j] = val except ValueError: - self.assertTrue(i<=j or val >= 2**(i-j)) + assert i<=j or val >= 2**(i-j) continue else: ref = long(setSlice(s, i, j, extv), 2) - self.assertEqual(bv, ref) + assert bv == ref mask = (2**(i-j))-1 vali = val ^ mask try: bvi[i:j] = vali except ValueError: - self.assertTrue(vali >= 2**(i-j)) + assert vali >= 2**(i-j) continue else: refi = ~long(setSlice(s, i, j, extv), 2) - self.assertEqual(bvi, refi) - + assert bvi == refi + def testSetSliceLeftOpen(self): self.seqsSetup() - toggle = 0 + toggle = 0 for s in self.seqs: n = long(s, 2) for j in range(0, len(s)+5): @@ -224,13 +228,12 @@ class TestIntBvIndexing(TestCase): bv[:j] = val bvi[:j] = -1-val ref = long(setSliceLeftOpen(s, j, v), 2) - self.assertEqual(bv, ref) + assert bv == ref refi = ~long(setSliceLeftOpen(s, j, v), 2) - self.assertEqual(bvi, refi) - - + assert bvi == refi -class TestIntBvAsInt(TestCase): + +class TestIntBvAsInt: def seqSetup(self, imin, imax, jmin=0, jmax=None): seqi = [imin, imin, 12, 34] @@ -264,7 +267,7 @@ class TestIntBvAsInt(TestCase): seqj.append(j) self.seqi = seqi self.seqj = seqj - + def binaryCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) for i, j in zip(self.seqi, self.seqj): @@ -277,9 +280,9 @@ class TestIntBvAsInt(TestCase): #self.assertEqual(type(r1), intbv) #self.assertEqual(type(r2), intbv) #self.assertEqual(type(r3), intbv) - self.assertEqual(r1, ref) - self.assertEqual(r2, ref) - self.assertEqual(r3, ref) + assert r1 == ref + assert r2 == ref + assert r3 == ref def augmentedAssignCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) @@ -293,14 +296,14 @@ class TestIntBvAsInt(TestCase): r2 = op(r2, bj) r3 = bi3 = intbv(long(i)) r3 = op(r3, bj) - self.assertEqual(type(r1), intbv) - self.assertEqual(type(r3), intbv) - self.assertEqual(r1, ref) - self.assertEqual(r2, ref) - self.assertEqual(r3, ref) - self.assertTrue(r1 is bi1) - self.assertTrue(r3 is bi3) - + assert type(r1) == intbv + assert type(r3) == intbv + assert r1 == ref + assert r2 == ref + assert r3 == ref + assert r1 is bi1 + assert r3 is bi3 + def unaryCheck(self, op, imin=0, imax=None): self.seqSetup(imin=imin, imax=imax) for i in self.seqi: @@ -308,17 +311,17 @@ class TestIntBvAsInt(TestCase): ref = op(i) r1 = op(bi) #self.assertEqual(type(r1), intbv) - self.assertEqual(r1, ref) - + assert r1 == ref + def conversionCheck(self, op, imin=0, imax=None): self.seqSetup(imin=imin, imax=imax) for i in self.seqi: bi = intbv(i) ref = op(i) r1 = op(bi) - self.assertEqual(type(r1), type(ref)) - self.assertEqual(r1, ref) - + assert type(r1) == type(ref) + assert r1 == ref + def comparisonCheck(self, op, imin=0, imax=None, jmin=0, jmax=None): self.seqSetup(imin=imin, imax=imax, jmin=jmin, jmax=jmax) for i, j in zip(self.seqi, self.seqj): @@ -328,9 +331,9 @@ class TestIntBvAsInt(TestCase): r1 = op(bi, j) r2 = op(i, bj) r3 = op(bi, bj) - self.assertEqual(r1, ref) - self.assertEqual(r2, ref) - self.assertEqual(r3, ref) + assert r1 == ref + assert r2 == ref + assert r3 == ref def testAdd(self): self.binaryCheck(operator.add) @@ -339,14 +342,14 @@ class TestIntBvAsInt(TestCase): self.binaryCheck(operator.sub) def testMul(self): - self.binaryCheck(operator.mul, imax=maxint) # XXX doesn't work for long i??? + self.binaryCheck(operator.mul, imax=maxint) # XXX doesn't work for long i??? def testTrueDiv(self): self.binaryCheck(operator.truediv, jmin=1) - + def testFloorDiv(self): self.binaryCheck(operator.floordiv, jmin=1) - + def testMod(self): self.binaryCheck(operator.mod, jmin=1) @@ -355,7 +358,7 @@ class TestIntBvAsInt(TestCase): def testLShift(self): self.binaryCheck(operator.lshift, jmax=256) - + def testRShift(self): self.binaryCheck(operator.rshift, jmax=256) @@ -364,7 +367,7 @@ class TestIntBvAsInt(TestCase): def testOr(self): self.binaryCheck(operator.or_) - + def testXor(self): self.binaryCheck(operator.xor) @@ -373,13 +376,13 @@ class TestIntBvAsInt(TestCase): def testISub(self): self.augmentedAssignCheck(operator.isub) - + def testIMul(self): - self.augmentedAssignCheck(operator.imul, imax=maxint) #XXX doesn't work for long i??? - + self.augmentedAssignCheck(operator.imul, imax=maxint) # XXX doesn't work for long i??? + def testIFloorDiv(self): self.augmentedAssignCheck(operator.ifloordiv, jmin=1) - + def testIMod(self): self.augmentedAssignCheck(operator.imod, jmin=1) @@ -388,22 +391,22 @@ class TestIntBvAsInt(TestCase): def testIAnd(self): self.augmentedAssignCheck(operator.iand) - + def testIOr(self): self.augmentedAssignCheck(operator.ior) - + def testIXor(self): self.augmentedAssignCheck(operator.ixor) - + def testILShift(self): self.augmentedAssignCheck(operator.ilshift, jmax=256) - + def testIRShift(self): self.augmentedAssignCheck(operator.irshift, jmax=256) def testNeg(self): self.unaryCheck(operator.neg) - + def testNeg(self): self.unaryCheck(operator.pos) @@ -415,52 +418,50 @@ class TestIntBvAsInt(TestCase): def testInt(self): self.conversionCheck(int, imax=maxint) - + def testLong(self): self.conversionCheck(long) - + def testFloat(self): self.conversionCheck(float) # XXX __complex__ seems redundant ??? (complex() works as such?) - + def testOct(self): self.conversionCheck(oct) - + def testHex(self): self.conversionCheck(hex) def testLt(self): self.comparisonCheck(operator.lt) + def testLe(self): self.comparisonCheck(operator.le) + def testGt(self): self.comparisonCheck(operator.gt) + def testGe(self): self.comparisonCheck(operator.ge) + def testEq(self): self.comparisonCheck(operator.eq) + def testNe(self): self.comparisonCheck(operator.ne) - - -class TestIntbvBounds(TestCase): - + + +class TestIntbvBounds: + def testConstructor(self): - self.assertEqual(intbv(40, max=54), 40) - try: + assert intbv(40, max=54) == 40 + with pytest.raises(ValueError): intbv(40, max=27) - except ValueError: - pass - else: - self.fail() - self.assertEqual(intbv(25, min=16), 25) - try: + + assert intbv(25, min=16) == 25 + with pytest.raises(ValueError): intbv(25, min=27) - except ValueError: - pass - else: - self.fail() def testSliceAssign(self): a = intbv(min=-24, max=34) @@ -468,79 +469,64 @@ class TestIntbvBounds(TestCase): for k in (6, 9, 10): a[:] = 0 a[k:] = i - self.assertEqual(a, i) + assert a == i for i in (-25, -128, 34, 35, 229): for k in (0, 9, 10): - try: + with pytest.raises(ValueError): a[k:] = i - except ValueError: - pass - else: - self.fail() + a = intbv(5)[8:] for v in (0, 2**8-1, 100): a[:] = v for v in (-1, 2**8, -10, 1000): - try: + with pytest.raises(ValueError): a[:] = v - except ValueError: - pass - else: - self.fail() def checkBounds(self, i, j, op): a = intbv(i) - self.assertEqual(a, i) # just to be sure + assert a == i # just to be sure try: exec("a %s long(j)" % op) except (ZeroDivisionError, ValueError): - return # prune + return # prune if not isinstance(a._val, integer_types): - return # prune + return # prune if abs(a) > maxint * maxint: - return # keep it reasonable + return # keep it reasonable if a > i: b = intbv(i, min=i, max=a+1) for m in (i+1, a): b = intbv(i, min=i, max=m) - try: + with pytest.raises(ValueError): exec("b %s long(j)" % op) - except ValueError: - pass - else: - self.fail() elif a < i : b = intbv(i, min=a, max=i+1) - exec("b %s long(j)" % op) # should be ok + exec("b %s long(j)" % op) # should be ok for m in (a+1, i): b = intbv(i, min=m, max=i+1) - try: + with pytest.raises(ValueError): exec("b %s long(j)" % op) - except ValueError: - pass - else: - self.fail() - else: # a == i + else: # a == i b = intbv(i, min=i, max=i+1) - exec("b %s long(j)" % op) # should be ok - + exec("b %s long(j)" % op) # should be ok + def checkOp(self, op): for i in (0, 1, -1, 2, -2, 16, -24, 129, -234, 1025, -15660): for j in (0, 1, -1, 2, -2, 9, -15, 123, -312, 2340, -23144): self.checkBounds(i, j, op) - + def testIAdd(self): self.checkOp("+=") def testISub(self): self.checkOp("-=") - + def testIMul(self): - self.checkOp("*=") - + self.checkOp("*=") + def testIFloorDiv(self): self.checkOp("//=") - + def testIMod(self): self.checkOp("%=") @@ -549,21 +535,21 @@ class TestIntbvBounds(TestCase): def testIAnd(self): self.checkOp("&=") - + def testIOr(self): self.checkOp("|=") - + def testIXor(self): self.checkOp("^=") - + def testILShift(self): self.checkOp("<<=") - + def testIRShift(self): self.checkOp(">>=") -class TestIntbvCopy(TestCase): +class TestIntbvCopy: def testCopy(self): @@ -573,15 +559,8 @@ class TestIntbvCopy(TestCase): b = copy(n) c = deepcopy(n) for m in (a, b, c): - self.assertEqual(n, m) - self.assertEqual(n._val, m._val) - self.assertEqual(n.min, m.min) - self.assertEqual(n.max, m.max) - self.assertEqual(len(n), len(m)) - - - -if __name__ == "__main__": - unittest.main() - - + assert n == m + assert n._val == m._val + assert n.min == m.min + assert n.max == m.max + assert len(n) == len(m) diff --git a/myhdl/test/core/test_misc.py b/myhdl/test/core/test_misc.py index 6a0c541d..d019454a 100644 --- a/myhdl/test/core/test_misc.py +++ b/myhdl/test/core/test_misc.py @@ -20,29 +20,27 @@ """ Run the unit tests for Signal """ from __future__ import absolute_import - import random -from random import randrange -random.seed(1) # random, but deterministic -from types import GeneratorType - -import unittest -from unittest import TestCase from myhdl import instance, instances +random.seed(1) # random, but deterministic + + def A(n): @instance def logic(): yield None return logic + def B(n): @instance def logic(): yield None return logic + def C(n): A_1 = A(1) A_2 = A(2) @@ -51,7 +49,8 @@ def C(n): g = 3 -class InstancesTest(TestCase): + +class TestInstances: def testInstances(self): @@ -70,10 +69,6 @@ class InstancesTest(TestCase): i = instances() # can't just construct an expected list; # that would become part of the instances also! - self.assertEqual(len(i), 4) + assert len(i) == 4 for e in (D_1, A_1, B_1, C_1): - self.assertTrue(e in i) - - -if __name__ == "__main__": - unittest.main() + assert e in i diff --git a/myhdl/test/core/test_modbv.py b/myhdl/test/core/test_modbv.py index 1b3d2d14..a8c3a189 100644 --- a/myhdl/test/core/test_modbv.py +++ b/myhdl/test/core/test_modbv.py @@ -20,61 +20,48 @@ """ Run the modbv unit tests. """ from __future__ import absolute_import - -import unittest -from unittest import TestCase +import pytest from myhdl._intbv import intbv from myhdl._modbv import modbv -class TestModbvWrap(TestCase): + +class TestModbvWrap: def testWrap(self): x = modbv(0, min=-8, max=8) x[:] = x + 1 - self.assertEqual(1, x) + assert 1 == x x[:] = x + 2 - self.assertEqual(3, x) + assert 3 == x x[:] = x + 5 - self.assertEqual(-8, x) + assert -8 == x x[:] = x + 1 - self.assertEqual(-7, x) + assert -7 == x x[:] = x - 5 - self.assertEqual(4, x) + assert 4 == x x[:] = x - 4 - self.assertEqual(0, x) + assert 0 == x x[:] += 15 x[:] = x - 1 - self.assertEqual(-2, x) - + assert -2 == x def testInit(self): - self.assertRaises(ValueError, intbv, 15, min=-8, max=8) + with pytest.raises(ValueError): + intbv(15, min=-8, max=8) + x = modbv(15, min=-8, max=8) - self.assertEqual(-1, x) + assert -1 == x # Arbitrary boundraries support (no exception) modbv(5, min=-3, max=8) - def testNoWrap(self): # Validate the base class fails for the wraps x = intbv(0, min=-8, max=8) - try: + with pytest.raises(ValueError): x[:] += 15 - self.fail() - except ValueError: - pass x = intbv(0, min=-8, max=8) - try: + with pytest.raises(ValueError): x[:] += 15 - self.fail() - except ValueError: - pass - - -if __name__ == "__main__": - unittest.main() - - diff --git a/myhdl/test/core/test_signed.py b/myhdl/test/core/test_signed.py index 7e496b8b..d0731834 100644 --- a/myhdl/test/core/test_signed.py +++ b/myhdl/test/core/test_signed.py @@ -21,15 +21,12 @@ """ Run the intbv.signed() unit tests. """ from __future__ import absolute_import -import unittest -from unittest import TestCase - from random import randrange from myhdl import * -class TestIntbvSigned(TestCase): +class TestIntbvSigned: '''Test cases to verify the intbv.signed() member function''' def testPlainIntbvInstance(self): @@ -60,7 +57,7 @@ class TestIntbvSigned(TestCase): # Expect the number to be returned a = intbv(0x3b, min=0, max=0x7c) b = a.signed() - self.assertEqual(b, 0x3b) + assert b == 0x3b # intbv with positive range, pos number, and msb set, return signed() # test various bit patterns to see that the 2's complement @@ -68,72 +65,71 @@ class TestIntbvSigned(TestCase): # Expect the number to be converted to a negative number a = intbv(7, min=0, max=8) b = a.signed() - self.assertEqual(b, -1) + assert b == -1 a = intbv(6, min=0, max=8) b = a.signed() - self.assertEqual(b, -2) + assert b == -2 a = intbv(5, min=0, max=8) b = a.signed() - self.assertEqual(b, -3) + assert b == -3 # set bit #3 and increase the range so that the set bit is considered # the sign bit. Here min = 0 # Expect to return -4 a = intbv(4, min=0, max=5) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=0, max=6) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=0, max=7) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=0, max=8) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 # here it is not the sign bit anymore # Expect the value to be 4 a = intbv(4, min=0, max=9) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 # set bit #3 and increase the range so that the set bit is considered # the sign bit. Here min > 0 # Expect to return -4 a = intbv(4, min=1, max=5) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=2, max=6) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=3, max=7) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 a = intbv(4, min=4, max=8) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 # again with min > 0, here it is not the sign bit anymore # Expect the value to be 4 a = intbv(4, min=2, max=9) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 # intbv with positive range, value = 0, return signed() # Expect the number to be returned a = intbv(0, min=0, max=0x8) b = a.signed() - self.assertEqual(b, 0) - + assert b == 0 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # in these cases the .signed() function should classify the @@ -144,71 +140,68 @@ class TestIntbvSigned(TestCase): # Expect value to be returned as is a = intbv(8) b = a.signed() - self.assertEqual(b, 8) + assert b == 8 # intbv without range, neg number, return signed() # Expect value to be returned as is a = intbv(-8) b = a.signed() - self.assertEqual(b, -8) + assert b == -8 # set bit #3 and increase the range that the set bit is actually the # msb, but due to the negative range not considered signed # Expect to return 4 a = intbv(4, min=-1, max=5) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 a = intbv(4, min=-1, max=6) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 a = intbv(4, min=-1, max=7) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 a = intbv(4, min=-1, max=8) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 # intbv with negative range, pos number, and msb set, return signed() # Expect the number to returned as is a = intbv(7, min=-1, max=8) b = a.signed() - self.assertEqual(b, 7) + assert b == 7 a = intbv(6, min=-1, max=8) b = a.signed() - self.assertEqual(b, 6) + assert b == 6 a = intbv(5, min=-1, max=8) b = a.signed() - self.assertEqual(b, 5) - + assert b == 5 # intbv with symmetric (min = -max) range, pos value, msb set # return signed() # Expect value returned as is a = intbv(4, min=-8, max=8) b = a.signed() - self.assertEqual(b, 4) + assert b == 4 # intbv with symmetric (min = -max) range, neg value, # return signed() # Expect value returned as is a = intbv(-4, min=-8, max=8) b = a.signed() - self.assertEqual(b, -4) + assert b == -4 # intbv with symmetric (min=-max) range, value = 0, # return signed() # Expect value returned as is a = intbv(0, min=-8, max=8) b = a.signed() - self.assertEqual(b, 0) + assert b == 0 - - def testSlicedSigned(self): '''Test a slice with .signed() @@ -218,15 +211,14 @@ class TestIntbvSigned(TestCase): ''' a = intbv(4, min=-8, max=8) b = a[4:] - self.assertEqual(b, 4) + assert b == 4 b = a[4:].signed() - self.assertEqual(b, 4) # msb is not set with a 4 bit slice + assert b == 4 # msb is not set with a 4 bit slice b = a[3:] - self.assertEqual(b, 4) + assert b == 4 b = a[3:].signed() - self.assertEqual(b, -4) # msb is set with 3 bits sliced - + assert b == -4 # msb is set with 3 bits sliced def testSignedConcat(self): '''Test the .signed() function with the concatenate function''' @@ -234,23 +226,22 @@ class TestIntbvSigned(TestCase): # concat 3 bits # Expect the signed function to return a negative value a = concat(True, True, True).signed() - self.assertEqual(a, -1) + assert a == -1 # concate a 3 bit intbv with msb set and two bits # Expect a negative number b = concat(intbv(5,min=0,max=8), True, True).signed() - self.assertEqual(b, -9) - + assert b == -9 def checkInvariants(self, a): """Check invariants of signed operator.""" W = len(a) b = intbv(a.signed()) if W > 0: - self.assertEqual(a[W:], b[W:]) - self.assertEqual(b[:W], -a[W-1]) + assert a[W:] == b[W:] + assert b[:W] == -a[W-1] else: - self.assertEqual(a, b) + assert a == b def testRandom(self): NRTESTS = 1000 @@ -262,11 +253,3 @@ class TestIntbvSigned(TestCase): a = intbv(v, min=lo, max=hi) self.checkInvariants(a) self.checkInvariants(Signal(a)) - - - -######################################################################## -# main -# -if __name__ == "__main__": - unittest.main() diff --git a/myhdl/test/core/test_traceSignals.py b/myhdl/test/core/test_traceSignals.py index 8688ced6..7f79c00a 100644 --- a/myhdl/test/core/test_traceSignals.py +++ b/myhdl/test/core/test_traceSignals.py @@ -20,24 +20,22 @@ """ Run the unit tests for traceSignals """ from __future__ import absolute_import - -import random -from random import randrange -random.seed(1) # random, but deterministic -import sys import os +import random + +import pytest + +from myhdl import Signal, Simulation, _simulator, delay, instance, intbv +from myhdl._traceSignals import TraceSignalsError, _error, traceSignals +from helpers import raises_kind + +random.seed(1) # random, but deterministic path = os.path -import unittest -from unittest import TestCase -import shutil -import glob - -from myhdl import delay, intbv, Signal, Simulation, _simulator, instance -from myhdl._traceSignals import traceSignals, TraceSignalsError, _error QUIET=1 + def gen(clk): @instance def logic(): @@ -46,26 +44,31 @@ def gen(clk): clk.next = not clk return logic + def fun(): clk = Signal(bool(0)) inst = gen(clk) return inst + def dummy(): clk = Signal(bool(0)) inst = gen(clk) return 1 + def top(): inst = traceSignals(fun) return inst + def top2(): inst = [{} for i in range(4)] j = 3 inst[j-2]['key'] = traceSignals(fun) return inst + def top3(): inst_1 = traceSignals(fun) inst_2 = traceSignals(fun) @@ -82,6 +85,7 @@ def genTristate(clk, x, y, z): while 1: yield delay(10) clk.next = not clk + @instance def logic(): for v in [True, False, None, 0, True, None, None, 1]: @@ -95,6 +99,7 @@ def genTristate(clk, x, y, z): yd.next = zd.next = 0 return ckgen,logic + def tristate(): from myhdl import TristateSignal clk = Signal(bool(0)) @@ -105,79 +110,54 @@ def tristate(): inst = genTristate(clk, x, y, z) return inst + def topTristate(): inst = traceSignals(tristate) return inst -class TestTraceSigs(TestCase): - def setUp(self): - paths = glob.glob("*.vcd") + glob.glob("*.vcd.*") - for p in paths: - os.remove(p) +@pytest.yield_fixture +def vcd_dir(tmpdir): + with tmpdir.as_cwd(): + yield tmpdir + if _simulator._tracing: + _simulator._tf.close() + _simulator._tracing = 0 - def tearDown(self): - paths = glob.glob("*.vcd") + glob.glob("*.vcd.*") - if _simulator._tracing: - _simulator._tf.close() - _simulator._tracing = 0 - #for p in paths: - # os.remove(p) -## def testTopName(self): -## p = "dut.vcd" -## dut = traceSignals(fun) -## _simulator._tf.close() -## _simulator._tracing = 0 -## try: -## traceSignals(fun) -## except TraceSignalsError, e: -## self.assertEqual(e.kind, _error.TopLevelName) -## else: -## self.fail() +class TestTraceSigs: - def testMultipleTraces(self): - try: + def testMultipleTraces(self, vcd_dir): + with raises_kind(TraceSignalsError, _error.MultipleTraces): dut = top3() - except TraceSignalsError as e: - self.assertEqual(e.kind, _error.MultipleTraces) - else: - self.fail() - def testArgType1(self): - try: + def testArgType1(self, vcd_dir): + with raises_kind(TraceSignalsError, _error.ArgType): dut = traceSignals([1, 2]) - except TraceSignalsError as e: - self.assertEqual(e.kind, _error.ArgType) - else: - self.fail() - def testReturnVal(self): + def testReturnVal(self, vcd_dir): from myhdl import ExtractHierarchyError from myhdl._extractHierarchy import _error - try: + kind = _error.InconsistentToplevel % (2, "dummy") + with raises_kind(ExtractHierarchyError, kind): dut = traceSignals(dummy) - except ExtractHierarchyError as e: - self.assertEqual(e.kind, _error.InconsistentToplevel % (2, "dummy")) - else: - self.fail() - def testHierarchicalTrace1(self): + def testHierarchicalTrace1(self, vcd_dir): p = "%s.vcd" % fun.__name__ top() - self.assertTrue(path.exists(p)) + assert path.exists(p) - def testHierarchicalTrace2(self): + def testHierarchicalTrace2(self, vcd_dir): pdut = "%s.vcd" % top.__name__ psub = "%s.vcd" % fun.__name__ dut = traceSignals(top) - self.assertTrue(path.exists(pdut)) - self.assertTrue(not path.exists(psub)) + assert path.exists(pdut) + assert not path.exists(psub) - def testTristateTrace(self): + def testTristateTrace(self, vcd_dir): Simulation(topTristate()).run(100, quiet=QUIET) - def testBackupOutputFile(self): + def testBackupOutputFile(self, vcd_dir): p = "%s.vcd" % fun.__name__ dut = traceSignals(fun) Simulation(dut).run(1000, quiet=QUIET) @@ -185,16 +165,24 @@ class TestTraceSigs(TestCase): _simulator._tracing = 0 size = path.getsize(p) pbak = p + '.' + str(path.getmtime(p)) - self.assertTrue(not path.exists(pbak)) + assert not path.exists(pbak) dut = traceSignals(fun) _simulator._tf.close() _simulator._tracing = 0 - self.assertTrue(path.exists(p)) - self.assertTrue(path.exists(pbak)) - self.assertTrue(path.getsize(pbak) == size) - self.assertTrue(path.getsize(p) < size) + assert path.exists(p) + assert path.exists(pbak) + assert path.getsize(pbak) == size + assert path.getsize(p) < size - - -if __name__ == "__main__": - unittest.main() + def testSetDirectory(self, vcd_dir): + traceSignals.directory = 'some_vcd_dir' + os.mkdir(path.join(str(vcd_dir), traceSignals.directory)) + pdut = "%s.vcd" % top.__name__ + psub = "%s.vcd" % fun.__name__ + pdutd = path.join(traceSignals.directory, "%s.vcd" % top.__name__) + psubd = path.join(traceSignals.directory, "%s.vcd" % fun.__name__) + dut = traceSignals(top) + assert not path.exists(pdut) + assert not path.exists(psub) + assert path.exists(pdutd) + assert not path.exists(psubd) diff --git a/myhdl/test/helpers.py b/myhdl/test/helpers.py new file mode 100644 index 00000000..2fdc2e14 --- /dev/null +++ b/myhdl/test/helpers.py @@ -0,0 +1,17 @@ +import pytest + + +class raises_kind(object): + def __init__(self, exc, kind): + self.exc = exc + self.kind = kind + + def __enter__(self): + return None + + def __exit__(self, *tp): + __tracebackhide__ = True + if tp[0] is None: + pytest.fail("DID NOT RAISE") + assert tp[1].kind == self.kind + return issubclass(tp[0], self.exc) diff --git a/myhdl/test/core/perf_inferWaiter.py b/scripts/benchmark/perf_inferWaiter.py similarity index 100% rename from myhdl/test/core/perf_inferWaiter.py rename to scripts/benchmark/perf_inferWaiter.py diff --git a/scripts/ci.sh b/scripts/ci.sh index 11b8b5db..93b3942c 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -23,15 +23,15 @@ echo -e "Running $CI_TARGET tests\n" CI_TARGET=${CI_TARGET:-core} if [ "$CI_TARGET" == "core" ]; then run_test make -C myhdl/test/core -elif [ "$CI_TARGET" == "icarus" ]; then - run_test make -C "myhdl/test/conversion/general" icarus +elif [ "$CI_TARGET" == "iverilog" ]; then + run_test make -C "myhdl/test/conversion/general" iverilog run_test make -C cosimulation/icarus test run_test make -C myhdl/test/conversion/toVerilog - run_test make -C "myhdl/test/bugs" icarus + run_test make -C "myhdl/test/bugs" iverilog elif [ "$CI_TARGET" == "ghdl" ]; then - run_test make -C "myhdl/test/conversion/general" GHDL - run_test make -C myhdl/test/conversion/toVHDL GHDL - run_test make -C "myhdl/test/bugs" GHDL + run_test make -C "myhdl/test/conversion/general" ghdl + run_test make -C myhdl/test/conversion/toVHDL ghdl + run_test make -C "myhdl/test/bugs" ghdl fi exit $foundError diff --git a/tox.ini b/tox.ini index 2f1dbc8c..75301868 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ envlist = py27,py34 [testenv] -deps = pytest -commands = py.test [] +deps = pytest-xdist +commands = py.test --basetemp={envtmpdir} {posargs} [testenv:docs] whitelist_externals = make