Generate VHDL report statements for $display

This changes the implementation of $display/$write to use VHDL
report statements rather the the std.textio functions. The code
produced is simpler and more like what a real VHDL designed would
write. However it no longer exactly matches the Verilog output as
most VHDL simulators prepend the text with simulation time, entity
name, severity level, etc. There is a corresponding change in
ivtest to support this.
This commit is contained in:
Nick Gasson 2010-08-24 22:13:08 +01:00
parent 0cec4495ca
commit 48ae8c1ce5
7 changed files with 132 additions and 213 deletions

View File

@ -50,7 +50,7 @@ dep:
mv $*.d dep
O = vhdl.o state.o vhdl_element.o vhdl_type.o vhdl_syntax.o scope.o process.o \
stmt.o expr.o lpm.o display.o support.o cast.o logic.o
stmt.o expr.o lpm.o support.o cast.o logic.o
ifeq (@WIN32@,yes)
TGTLDFLAGS=-L.. -livl

View File

@ -56,6 +56,8 @@ vhdl_expr *vhdl_expr::cast(const vhdl_type *to)
return to_vector(to->get_name(), to->get_width());
case VHDL_TYPE_STD_LOGIC:
return to_std_logic();
case VHDL_TYPE_STRING:
return to_string();
default:
assert(false);
}
@ -112,6 +114,25 @@ vhdl_expr *vhdl_expr::to_integer()
return conv;
}
vhdl_expr *vhdl_expr::to_string()
{
bool numeric = type_->get_name() == VHDL_TYPE_UNSIGNED
|| type_->get_name() == VHDL_TYPE_SIGNED;
if (numeric) {
vhdl_fcall *image = new vhdl_fcall("integer'image", vhdl_type::string());
image->add_expr(this->cast(vhdl_type::integer()));
return image;
}
else {
// Assume type'image exists
vhdl_fcall *image = new vhdl_fcall(type_->get_string() + "'image",
vhdl_type::string());
image->add_expr(this);
return image;
}
}
/*
* Convert a generic expression to a Boolean.
*/

View File

@ -1,200 +0,0 @@
/*
* VHDL implementation of $display.
*
* Copyright (C) 2008-2009 Nick Gasson (nick@nickg.me.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "vhdl_target.h"
#include <iostream>
#include <cstring>
#include <cassert>
#include <cctype>
#include <sstream>
static const char *DISPLAY_LINE = "Verilog_Display_Line";
/*
* Write a VHDL expression into the current display line.
*/
static void display_write(stmt_container *container, vhdl_expr *expr)
{
vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
vhdl_var_ref *ref =
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
write->add_expr(ref);
vhdl_type_name_t type = expr->get_type()->get_name();
if (type == VHDL_TYPE_SIGNED || type == VHDL_TYPE_UNSIGNED) {
vhdl_type integer(VHDL_TYPE_INTEGER);
write->add_expr(expr->cast(&integer));
}
else if (type != VHDL_TYPE_STRING) {
// Need to add a call to Type'Image for types not
// supported by std.textio
std::string name(expr->get_type()->get_string());
name += "'Image";
vhdl_fcall *cast
= new vhdl_fcall(name.c_str(), vhdl_type::string());
cast->add_expr(expr);
write->add_expr(cast);
}
else
write->add_expr(expr);
container->add_stmt(write);
}
/*
* Write the value of DISPLAY_LINE to the output.
*/
static void display_line(stmt_container *container)
{
vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
vhdl_var_ref *output_ref =
new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
write_line->add_expr(output_ref);
vhdl_var_ref *ref =
new vhdl_var_ref(DISPLAY_LINE, vhdl_type::line());
write_line->add_expr(ref);
container->add_stmt(write_line);
}
/*
* Parse an octal escape sequence.
*/
static char parse_octal(const char *p)
{
assert(*p && *(p+1) && *(p+2));
assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1)));
return (*p - '0') * 64
+ (*(p+1) - '0') * 8
+ (*(p+2) - '0') * 1;
}
static void flush_string(std::ostringstream &ss, stmt_container *container)
{
display_write(container, new vhdl_const_string(ss.str().c_str()));
// Clear the stream
ss.str("");
}
// This should display the hierarchical module name, but we don't support
// this in VHDL. So just emit a warning.
static void display_m(stmt_container* container)
{
cerr << "Warning: no VHDL translation for %m format code" << endl;
}
/*
* Generate VHDL for the $display system task.
* This is implemented using the functions in std.textio. Each
* parameter is written to a line variable in the process and
* then the line is written to the special variable `Output'
* (which represents the console). Subsequent $displays will
* use the same line variable.
*
* It's possible, although quite unlikely, that there will be
* name collision with an existing variable called
* `Verilog_Display_Line' -- do something about this?
*/
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline)
{
if (!proc->get_scope()->have_declared(DISPLAY_LINE)) {
vhdl_var_decl *line_var =
new vhdl_var_decl(DISPLAY_LINE, vhdl_type::line());
line_var->set_comment("For generating $display output");
proc->get_scope()->add_decl(line_var);
}
// Write the data into the line
int count = ivl_stmt_parm_count(stmt), i = 0;
while (i < count) {
// $display may have an empty parameter, in which case
// the expression will be null
// The behaviour here seems to be to output a space
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
if (net == NULL) {
display_write(container, new vhdl_const_string(" "));
continue;
}
if (ivl_expr_type(net) == IVL_EX_STRING) {
ostringstream ss;
for (const char *p = ivl_expr_string(net); *p; p++) {
if (*p == '\\') {
// Octal escape
char ch = parse_octal(p+1);
if (ch == '\n') {
flush_string(ss, container);
display_line(container);
}
else
ss << ch;
p += 3;
}
else if (*p == '%' && *(++p) != '%') {
flush_string(ss, container);
// Skip over width for now
while (isdigit(*p)) ++p;
switch (*p) {
case 'm':
display_m(container);
break;
default:
{
assert(i < count);
ivl_expr_t netp = ivl_stmt_parm(stmt, i++);
assert(netp);
vhdl_expr *base = translate_expr(netp);
if (NULL == base)
return 1;
display_write(container, base);
}
}
}
else
ss << *p;
}
// Call Write on any non-empty string data left in the buffer
if (!ss.str().empty())
display_write(container, new vhdl_const_string(ss.str().c_str()));
}
else {
vhdl_expr *base = translate_expr(net);
if (NULL == base)
return 1;
display_write(container, base);
}
}
if (newline)
display_line(container);
return 0;
}

View File

@ -30,6 +30,9 @@
#include <set>
#include <algorithm>
static void emit_wait_for_0(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, vhdl_expr *expr);
/*
* VHDL has no real equivalent of Verilog's $finish task. The
* current solution is to use `assert false ...' to terminate
@ -57,6 +60,106 @@ static int draw_stask_finish(vhdl_procedural *proc, stmt_container *container,
return 0;
}
static char parse_octal(const char *p)
{
assert(*p && *(p+1) && *(p+2));
assert(isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+1)));
return (*p - '0') * 64
+ (*(p+1) - '0') * 8
+ (*(p+2) - '0') * 1;
}
// Generate VHDL report statements for Verilog $display/$write
static int draw_stask_display(vhdl_procedural *proc,
stmt_container *container,
ivl_statement_t stmt)
{
vhdl_binop_expr *text = new vhdl_binop_expr(VHDL_BINOP_CONCAT,
vhdl_type::string());
const int count = ivl_stmt_parm_count(stmt);
int i = 0;
while (i < count) {
// $display may have an empty parameter, in which case
// the expression will be null
// The behaviour here seems to be to output a space
ivl_expr_t net = ivl_stmt_parm(stmt, i++);
if (net == NULL) {
text->add_expr(new vhdl_const_string(" "));
continue;
}
if (ivl_expr_type(net) == IVL_EX_STRING) {
ostringstream ss;
for (const char *p = ivl_expr_string(net); *p; p++) {
if (*p == '\\') {
// Octal escape
char ch = parse_octal(p+1);
if (ch == '\n') {
// Is there a better way of handling newlines?
// Maybe generate another report statement
}
else
ss << ch;
p += 3;
}
else if (*p == '%' && *(++p) != '%') {
// Flush the output string up to this point
text->add_expr(new vhdl_const_string(ss.str()));
ss.str("");
// Skip over width for now
while (isdigit(*p)) ++p;
switch (*p) {
case 'm':
// TOOD: we can get the module name via attributes
cerr << "Warning: no VHDL translation for %m format code"
<< endl;
break;
default:
{
assert(i < count);
ivl_expr_t netp = ivl_stmt_parm(stmt, i++);
assert(netp);
vhdl_expr *base = translate_expr(netp);
if (NULL == base)
return 1;
emit_wait_for_0(proc, container, stmt, base);
text->add_expr(base->cast(text->get_type()));
}
}
}
else
ss << *p;
}
// Emit any non-empty string data left in the buffer
if (!ss.str().empty())
text->add_expr(new vhdl_const_string(ss.str()));
}
else {
vhdl_expr *base = translate_expr(net);
if (NULL == base)
return 1;
emit_wait_for_0(proc, container, stmt, base);
text->add_expr(base->cast(text->get_type()));
}
}
if (count == 0)
text->add_expr(new vhdl_const_string(""));
container->add_stmt(new vhdl_report_stmt(text));
return 0;
}
/*
* Generate VHDL for system tasks (like $display). Not all of
* these are supported.
@ -67,9 +170,9 @@ static int draw_stask(vhdl_procedural *proc, stmt_container *container,
const char *name = ivl_stmt_name(stmt);
if (strcmp(name, "$display") == 0)
return draw_stask_display(proc, container, stmt, true);
return draw_stask_display(proc, container, stmt);
else if (strcmp(name, "$write") == 0)
return draw_stask_display(proc, container, stmt, false);
return draw_stask_display(proc, container, stmt);
else if (strcmp(name, "$finish") == 0)
return draw_stask_finish(proc, container, stmt);
else {

View File

@ -135,7 +135,6 @@ void vhdl_entity::emit(std::ostream &of, int level) const
of << "library ieee;" << std::endl;
of << "use ieee.std_logic_1164.all;" << std::endl;
of << "use ieee.numeric_std.all;" << std::endl;
of << "use std.textio.all;" << std::endl;
of << std::endl;
emit_comment(of, level);
@ -620,11 +619,7 @@ void vhdl_var_ref::emit(std::ostream &of, int level) const
void vhdl_const_string::emit(std::ostream &of, int level) const
{
// In some instances a string literal can be ambiguous between
// a String type and some other types (e.g. std_logic_vector)
// The explicit cast to String removes this ambiguity (although
// isn't always strictly necessary)
of << "String'(\"" << value_ << "\")";
of << "\"" << value_ << "\"";
}
void vhdl_null_stmt::emit(std::ostream &of, int level) const

View File

@ -51,6 +51,7 @@ public:
virtual vhdl_expr *to_integer();
virtual vhdl_expr *to_std_logic();
virtual vhdl_expr *to_vector(vhdl_type_name_t name, int w);
virtual vhdl_expr *to_string();
virtual void find_vars(vhdl_var_set_t& read) {}
protected:
@ -175,7 +176,7 @@ private:
class vhdl_const_string : public vhdl_expr {
public:
vhdl_const_string(const char *value)
vhdl_const_string(const string& value)
: vhdl_expr(vhdl_type::string(), true), value_(value) {}
void emit(std::ostream &of, int level) const;
@ -268,7 +269,7 @@ private:
*/
class vhdl_fcall : public vhdl_expr {
public:
vhdl_fcall(const char *name, vhdl_type *rtype)
vhdl_fcall(const string& name, vhdl_type *rtype)
: vhdl_expr(rtype), name_(name) {};
~vhdl_fcall() {}

View File

@ -1,3 +1,4 @@
// -*- mode: c++ -*-
#ifndef INC_VHDL_TARGET_H
#define INC_VHDL_TARGET_H
@ -29,8 +30,6 @@ vhdl_var_ref *nexus_to_var_ref(vhdl_scope *arch_scope, ivl_nexus_t nexus);
vhdl_var_ref* readable_ref(vhdl_scope* scope, ivl_nexus_t nex);
string make_safe_name(ivl_signal_t sig);
int draw_stask_display(vhdl_procedural *proc, stmt_container *container,
ivl_statement_t stmt, bool newline = true);
void require_support_function(support_function_t f);
#endif /* #ifndef INC_VHDL_TARGET_H */