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:
parent
0cec4495ca
commit
48ae8c1ce5
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
107
tgt-vhdl/stmt.cc
107
tgt-vhdl/stmt.cc
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue