mirror of
https://github.com/myhdl/myhdl.git
synced 2024-12-14 07:44:38 +08:00
Merge branch 'master' into initial_value_support
This commit is contained in:
commit
c8b1b285ca
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,6 +22,7 @@ modelsim.ini
|
||||
transcript
|
||||
*.log
|
||||
work/
|
||||
work_nvc/
|
||||
work_vlog/
|
||||
work_vcom/
|
||||
*.wlf
|
||||
|
19
.travis.yml
19
.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
|
||||
|
||||
|
@ -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
|
||||
-------------------------
|
||||
|
||||
|
13
README.md
13
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
|
||||
------------
|
||||
|
21
cosimulation/modelsim-win/Makefile
Normal file
21
cosimulation/modelsim-win/Makefile
Normal file
@ -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 ||:
|
532
cosimulation/modelsim-win/myhdl_vpi.c
Normal file
532
cosimulation/modelsim-win/myhdl_vpi.c
Normal file
@ -0,0 +1,532 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
#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]();
|
||||
}
|
||||
}
|
11
cosimulation/modelsim-win/test/bin2gray.py
Normal file
11
cosimulation/modelsim-win/test/bin2gray.py
Normal file
@ -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)
|
2
cosimulation/modelsim-win/test/cosim.do
Normal file
2
cosimulation/modelsim-win/test/cosim.do
Normal file
@ -0,0 +1,2 @@
|
||||
run -all
|
||||
quit
|
11
cosimulation/modelsim-win/test/dff.py
Normal file
11
cosimulation/modelsim-win/test/dff.py
Normal file
@ -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())
|
11
cosimulation/modelsim-win/test/dff_clkout.py
Normal file
11
cosimulation/modelsim-win/test/dff_clkout.py
Normal file
@ -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())
|
11
cosimulation/modelsim-win/test/inc.py
Normal file
11
cosimulation/modelsim-win/test/inc.py
Normal file
@ -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())
|
47
cosimulation/modelsim-win/test/test_all.py
Normal file
47
cosimulation/modelsim-win/test/test_all.py
Normal file
@ -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()
|
@ -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<something> 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
|
||||
|
@ -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]();
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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`
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
8
doc/source/python3.rst
Normal file
8
doc/source/python3.rst
Normal file
@ -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.
|
@ -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
|
||||
|
587
doc/source/whatsnew/0.5.rst
Normal file
587
doc/source/whatsnew/0.5.rst
Normal file
@ -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():
|
||||
<generator body>
|
||||
...
|
||||
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():
|
||||
<generator body>
|
||||
...
|
||||
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()
|
||||
<body>
|
||||
...
|
||||
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():
|
||||
<combinatorial body>
|
||||
...
|
||||
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
|
@ -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
|
||||
|
||||
|
||||
|
@ -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())
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)()
|
@ -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)
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
33
myhdl/_getcellvars.py
Normal file
33
myhdl/_getcellvars.py
Normal file
@ -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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
89
myhdl/_visitors.py
Normal file
89
myhdl/_visitors.py
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -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"
|
@ -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
|
||||
|
0
myhdl/test/bugs/__init__.py
Normal file
0
myhdl/test/bugs/__init__.py
Normal file
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify
|
||||
|
||||
verify.simulator = "cver"
|
@ -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"
|
31
myhdl/test/bugs/test_issue134.py
Normal file
31
myhdl/test/bugs/test_issue134.py
Normal file
@ -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
|
49
myhdl/test/bugs/test_issue_117.py
Normal file
49
myhdl/test/bugs/test_issue_117.py
Normal file
@ -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()
|
||||
|
||||
|
30
myhdl/test/bugs/test_issue_122.py
Normal file
30
myhdl/test/bugs/test_issue_122.py
Normal file
@ -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
|
||||
|
148
myhdl/test/bugs/test_issue_127.py
Normal file
148
myhdl/test/bugs/test_issue_127.py
Normal file
@ -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)
|
||||
|
19
myhdl/test/bugs/test_issue_133.py
Normal file
19
myhdl/test/bugs/test_issue_133.py
Normal file
@ -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
|
@ -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
|
||||
|
||||
|
34
myhdl/test/bugs/test_issue_98.py
Normal file
34
myhdl/test/bugs/test_issue_98.py
Normal file
@ -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
|
||||
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "vcom"
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "vlog"
|
41
myhdl/test/conftest.py
Normal file
41
myhdl/test/conftest.py
Normal file
@ -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)
|
@ -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
|
||||
|
||||
|
@ -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)
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "GHDL"
|
@ -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
|
||||
|
0
myhdl/test/conversion/general/__init__.py
Normal file
0
myhdl/test/conversion/general/__init__.py
Normal file
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "cver"
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "icarus"
|
@ -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()
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "vcom"
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "vlog"
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify
|
||||
|
||||
verify.simulator = "GHDL"
|
@ -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
|
||||
|
0
myhdl/test/conversion/toVHDL/__init__.py
Normal file
0
myhdl/test/conversion/toVHDL/__init__.py
Normal file
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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():
|
||||
|
@ -1,4 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
from myhdl.conversion import verify, analyze
|
||||
|
||||
verify.simulator = analyze.simulator = "vcom"
|
0
myhdl/test/conversion/toVerilog/__init__.py
Normal file
0
myhdl/test/conversion/toVerilog/__init__.py
Normal file
@ -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
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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]
|
||||
|
@ -32,7 +32,7 @@ from unittest import TestCase
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
QUIET = 1
|
||||
|
||||
|
@ -9,7 +9,7 @@ random.seed(2)
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -9,7 +9,7 @@ random.seed(2)
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
|
@ -9,7 +9,7 @@ random.seed(2)
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
|
@ -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__':
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)])
|
||||
|
@ -9,7 +9,7 @@ random.seed(2)
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
|
@ -9,7 +9,7 @@ random.seed(2)
|
||||
|
||||
from myhdl import *
|
||||
|
||||
from util import setupCosimulation
|
||||
from .util import setupCosimulation
|
||||
|
||||
ACTIVE_LOW, INACTIVE_HIGH = 0, 1
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user