iverilog/tgt-vvp/vvp_process.c

437 lines
11 KiB
C
Raw Normal View History

2001-03-19 02:20:46 +01:00
/*
* Copyright (c) 2001 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: vvp_process.c,v 1.12 2001/03/30 05:49:53 steve Exp $"
2001-03-19 02:20:46 +01:00
#endif
# include "vvp_priv.h"
# include <assert.h>
static int show_statement(ivl_statement_t net);
2001-03-19 02:20:46 +01:00
static unsigned local_count = 0;
static unsigned thread_count = 0;
2001-03-19 02:20:46 +01:00
/*
* This file includes the code needed to generate VVP code for
* processes. Scopes are already declared, we generate here the
* executable code for the processes.
*/
2001-03-23 02:54:32 +01:00
unsigned bitchar_to_idx(char bit)
{
switch (bit) {
case '0':
return 0;
case '1':
return 1;
case 'x':
return 2;
case 'z':
return 3;
default:
assert(0);
return 0;
}
}
/*
* These functions handle the blocking assignment. Use the %set
* instruction to perform the actual assignment, and calculate any
* lvalues and rvalues that need calculating.
*
* The set_to_nexus function takes a particular nexus and generates
* the %set statements to assign the value.
*
* The show_stmt_assign function looks at the assign statement, scans
* the l-values, and matches bits of the r-value with the correct
* nexus.
*/
2001-03-23 02:54:32 +01:00
static void set_to_nexus(ivl_nexus_t nex, unsigned bit)
{
unsigned idx;
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
unsigned pin = ivl_nexus_ptr_pin(ptr);
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
if (sig == 0)
continue;
2001-03-23 02:54:32 +01:00
fprintf(vvp_out, " %%set V_%s[%u], %u;\n",
ivl_signal_name(sig), pin, bit);
}
}
static int show_stmt_assign(ivl_statement_t net)
{
ivl_lval_t lval;
ivl_expr_t rval = ivl_stmt_rval(net);
/* Handle the special case that the r-value is a constant. We
can generate the %set statement directly, without any worry
about generating code to evaluate the r-value expressions. */
if (ivl_expr_type(rval) == IVL_EX_NUMBER) {
unsigned idx;
const char*bits = ivl_expr_bits(rval);
/* XXXX Only single l-value supported for now */
assert(ivl_stmt_lvals(net) == 1);
lval = ivl_stmt_lval(net, 0);
/* XXXX No mux support yet. */
assert(ivl_lval_mux(lval) == 0);
for (idx = 0 ; idx < ivl_lval_pins(lval) ; idx += 1)
2001-03-23 02:54:32 +01:00
set_to_nexus(ivl_lval_pin(lval, idx),
bitchar_to_idx(bits[idx]));
return 0;
}
2001-03-23 02:54:32 +01:00
{ struct vector_info res = draw_eval_expr(rval);
unsigned wid = res.wid;
unsigned idx;
/* XXXX Only single l-value supported for now */
assert(ivl_stmt_lvals(net) == 1);
lval = ivl_stmt_lval(net, 0);
/* XXXX No mux support yet. */
assert(ivl_lval_mux(lval) == 0);
if (ivl_lval_pins(lval) < wid)
wid = ivl_lval_pins(lval);
for (idx = 0 ; idx < wid ; idx += 1)
set_to_nexus(ivl_lval_pin(lval, idx), res.base+idx);
for (idx = wid ; idx < ivl_lval_pins(lval) ; idx += 1)
set_to_nexus(ivl_lval_pin(lval, idx), 0);
}
return 0;
}
static int show_stmt_condit(ivl_statement_t net)
{
int rc = 0;
unsigned lab_false, lab_out;
ivl_expr_t exp = ivl_stmt_cond_expr(net);
struct vector_info cond = draw_eval_expr(exp);
assert(cond.wid == 1);
lab_false = local_count++;
lab_out = local_count++;
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, " %%jmp/0xz T_%d.%d, %u;\n",
thread_count, lab_false, cond.base);
rc += show_statement(ivl_stmt_cond_true(net));
if (ivl_stmt_cond_false(net)) {
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_out);
fprintf(vvp_out, "T_%d.%u\n", thread_count, lab_false);
rc += show_statement(ivl_stmt_cond_false(net));
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, "T_%d.%u\n", thread_count, lab_out);
} else {
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, "T_%d.%u\n", thread_count, lab_false);
}
return rc;
}
/*
* The delay statement is easy. Simply write a ``%delay <n>''
* instruction to delay the thread, then draw the included statement.
* The delay statement comes from verilog code like this:
*
* ...
* #<delay> <stmt>;
*/
static int show_stmt_delay(ivl_statement_t net)
{
int rc = 0;
unsigned long delay = ivl_stmt_delay_val(net);
ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
fprintf(vvp_out, " %%delay %lu;\n", delay);
rc += show_statement(stmt);
return rc;
}
static int show_stmt_fork(ivl_statement_t net)
{
unsigned idx;
int rc = 0;
static int transient_id = 0;
unsigned cnt = ivl_stmt_block_count(net);
int out = transient_id++;
/* Draw a fork statement for all but one of the threads of the
fork/join. Send the threads off to a bit of code where they
are implemented. */
for (idx = 0 ; idx < cnt-1 ; idx += 1) {
fprintf(vvp_out, " %%fork t_%u;\n", transient_id+idx);
}
/* Draw code to execute the remaining thread in the current
thread, then generate enough joins to merge back together. */
rc += show_statement(ivl_stmt_block_stmt(net, cnt-1));
for (idx = 0 ; idx < cnt-1 ; idx += 1) {
fprintf(vvp_out, " %%join;\n");
}
fprintf(vvp_out, " %%jmp t_%u;\n", out);
for (idx = 0 ; idx < cnt-1 ; idx += 1) {
fprintf(vvp_out, "t_%u\n", transient_id+idx);
rc += show_statement(ivl_stmt_block_stmt(net, idx));
fprintf(vvp_out, " %%end;\n");
}
/* This is the label for the out. Use this to branck around
the implementations of all the child threads. */
fprintf(vvp_out, "t_%u\n", out);
return rc;
}
/*
* noop statements are implemented by doing nothing.
*/
static int show_stmt_noop(ivl_statement_t net)
{
return 0;
}
2001-03-19 02:20:46 +01:00
2001-03-29 05:47:38 +02:00
static int show_stmt_trigger(ivl_statement_t net)
{
ivl_event_t ev = ivl_stmt_event(net);
assert(ev);
fprintf(vvp_out, " %%set E_%s, 0;\n", ivl_event_name(ev));
return 0;
}
2001-03-27 08:27:40 +02:00
static int show_stmt_wait(ivl_statement_t net)
{
ivl_event_t ev = ivl_stmt_event(net);
fprintf(vvp_out, " %%wait E_%s;\n", ivl_event_name(ev));
2001-03-27 08:27:40 +02:00
return show_statement(ivl_stmt_sub_stmt(net));
}
static int show_system_task_call(ivl_statement_t net)
2001-03-19 02:20:46 +01:00
{
unsigned idx;
unsigned parm_count = ivl_stmt_parm_count(net);
if (parm_count == 0) {
fprintf(vvp_out, " %%vpi_call \"%s\";\n", ivl_stmt_name(net));
return 0;
2001-03-19 02:20:46 +01:00
}
fprintf(vvp_out, " %%vpi_call \"%s\"", ivl_stmt_name(net));
for (idx = 0 ; idx < parm_count ; idx += 1) {
ivl_expr_t expr = ivl_stmt_parm(net, idx);
switch (ivl_expr_type(expr)) {
2001-03-25 05:24:10 +02:00
case IVL_EX_SIGNAL:
fprintf(vvp_out, ", V_%s", ivl_expr_name(expr));
break;
2001-03-19 02:20:46 +01:00
case IVL_EX_STRING:
fprintf(vvp_out, ", \"%s\"", ivl_expr_string(expr));
break;
default:
fprintf(vvp_out, ", ?");
break;
}
}
fprintf(vvp_out, ";\n");
return 0;
2001-03-19 02:20:46 +01:00
}
/*
* This function draws a statement as vvp assembly. It basically
* switches on the statement type and draws code based on the type and
* further specifics.
*/
static int show_statement(ivl_statement_t net)
2001-03-19 02:20:46 +01:00
{
const ivl_statement_type_t code = ivl_statement_type(net);
int rc = 0;
2001-03-19 02:20:46 +01:00
switch (code) {
case IVL_ST_ASSIGN:
rc += show_stmt_assign(net);
break;
2001-03-19 02:20:46 +01:00
/* Begin-end blocks simply draw their contents. */
case IVL_ST_BLOCK: {
unsigned idx;
unsigned cnt = ivl_stmt_block_count(net);
for (idx = 0 ; idx < cnt ; idx += 1) {
rc += show_statement(ivl_stmt_block_stmt(net, idx));
2001-03-19 02:20:46 +01:00
}
break;
}
case IVL_ST_CONDIT:
rc += show_stmt_condit(net);
break;
case IVL_ST_DELAY:
rc += show_stmt_delay(net);
break;
case IVL_ST_FORK:
rc += show_stmt_fork(net);
break;
case IVL_ST_NOOP:
rc += show_stmt_noop(net);
break;
2001-03-19 02:20:46 +01:00
case IVL_ST_STASK:
rc += show_system_task_call(net);
2001-03-19 02:20:46 +01:00
break;
2001-03-29 05:47:38 +02:00
case IVL_ST_TRIGGER:
rc += show_stmt_trigger(net);
break;
2001-03-27 08:27:40 +02:00
case IVL_ST_WAIT:
rc += show_stmt_wait(net);
break;
2001-03-19 02:20:46 +01:00
default:
fprintf(stderr, "vvp.tgt: Unable to draw statement type %u\n",
code);
rc += 1;
2001-03-19 02:20:46 +01:00
break;
}
return rc;
2001-03-19 02:20:46 +01:00
}
2001-03-27 08:27:40 +02:00
2001-03-19 02:20:46 +01:00
/*
* The process as a whole is surrounded by this code. We generate a
* start label that the .thread statement can use, and we generate
* code to terminate the thread.
*/
int draw_process(ivl_process_t net, void*x)
{
int rc = 0;
2001-03-20 02:44:13 +01:00
ivl_scope_t scope = ivl_process_scope(net);
2001-03-27 08:27:40 +02:00
ivl_statement_t stmt = ivl_process_stmt(net);
2001-03-20 02:44:13 +01:00
local_count = 0;
2001-03-20 02:44:13 +01:00
fprintf(vvp_out, " .scope S_%s;\n", ivl_scope_name(scope));
2001-03-19 02:20:46 +01:00
/* Generate the entry label. Just give the thread a number so
that we ar certain the label is unique. */
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, "T_%d\n", thread_count);
2001-03-19 02:20:46 +01:00
/* Draw the contents of the thread. */
2001-03-27 08:27:40 +02:00
rc += show_statement(stmt);
2001-03-19 02:20:46 +01:00
/* Terminate the thread with either an %end instruction (initial
statements) or a %jmp back to the beginning of the thread. */
switch (ivl_process_type(net)) {
case IVL_PR_INITIAL:
fprintf(vvp_out, " %%end;\n");
break;
case IVL_PR_ALWAYS:
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, " %%jmp T_%d;\n", thread_count);
2001-03-19 02:20:46 +01:00
break;
}
/* Now write out the .thread directive that tells vvp where
the thread starts. */
2001-03-27 08:27:40 +02:00
fprintf(vvp_out, " .thread T_%d;\n", thread_count);
2001-03-19 02:20:46 +01:00
thread_count += 1;
return rc;
2001-03-19 02:20:46 +01:00
}
/*
* $Log: vvp_process.c,v $
* Revision 1.12 2001/03/30 05:49:53 steve
* Generate code for fork/join statements.
*
2001-03-29 05:47:38 +02:00
* Revision 1.11 2001/03/29 03:47:38 steve
* Behavioral trigger statements.
*
* Revision 1.10 2001/03/28 06:07:40 steve
* Add the ivl_event_t to ivl_target, and use that to generate
* .event statements in vvp way ahead of the thread that uses it.
*
2001-03-27 08:27:40 +02:00
* Revision 1.9 2001/03/27 06:27:41 steve
* Generate code for simple @ statements.
*
* Revision 1.8 2001/03/27 03:31:07 steve
* Support error code from target_t::end_design method.
*
* Revision 1.7 2001/03/25 03:53:24 steve
* Skip true clause if condition ix 0, x or z
*
2001-03-25 05:24:10 +02:00
* Revision 1.6 2001/03/25 03:24:10 steve
* Draw signal inputs to system tasks.
*
2001-03-23 02:54:32 +01:00
* Revision 1.5 2001/03/23 01:54:32 steve
* assignments with non-trival r-values.
*
* Revision 1.4 2001/03/22 05:06:21 steve
* Geneate code for conditional statements.
*
* Revision 1.3 2001/03/21 01:49:43 steve
* Scan the scopes of a design, and draw behavioral
* blocking assignments of constants to vectors.
*
2001-03-20 02:44:13 +01:00
* Revision 1.2 2001/03/20 01:44:14 steve
* Put processes in the proper scope.
*
2001-03-19 02:20:46 +01:00
* Revision 1.1 2001/03/19 01:20:46 steve
* Add the tgt-vvp code generator target.
*
*/