Add support for LPM_SUBSTITUTE and wider LPM_MUX objects in vlog95 target.

This commit is contained in:
Martin Whitaker 2016-02-27 16:46:42 +00:00
parent 9d5f4ad048
commit a33255f0e0
2 changed files with 159 additions and 26 deletions

View File

@ -31,7 +31,7 @@ typedef enum lpm_sign_e {
} lpm_sign_t; } lpm_sign_t;
static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex,
int*base, unsigned*array_word); int*msb, int*lsb, unsigned*array_word);
/* /*
* Look to see if the nexus driver is signed. * Look to see if the nexus driver is signed.
@ -68,12 +68,12 @@ static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex)
* local signal to get the sign information. */ * local signal to get the sign information. */
if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) {
unsigned array_word = 0; unsigned array_word = 0;
int base = 0; int msb = 0, lsb = 0;
ivl_signal_t sig; ivl_signal_t sig;
assert(ivl_logic_pins(t_nlogic) == 2); assert(ivl_logic_pins(t_nlogic) == 2);
sig = nexus_is_signal(scope, sig = nexus_is_signal(scope,
ivl_logic_pin(t_nlogic, 0), ivl_logic_pin(t_nlogic, 0),
&base, &array_word); &msb, &lsb, &array_word);
assert(sig); assert(sig);
is_signed = ivl_signal_signed(sig); is_signed = ivl_signal_signed(sig);
} }
@ -88,6 +88,19 @@ static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex)
return is_signed; return is_signed;
} }
static unsigned get_nexus_width(ivl_nexus_t nex)
{
unsigned idx, count = ivl_nexus_ptrs(nex);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr);
if (sig) return ivl_signal_width(sig);
}
assert(0);
return 0;
}
static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm,
unsigned can_skip_unsigned) unsigned can_skip_unsigned)
{ {
@ -196,6 +209,14 @@ static void emit_gate_strength(ivl_net_logic_t nlogic, unsigned strength_type)
"gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); "gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic));
} }
static void emit_part_selector(int msb, int lsb)
{
if (msb != lsb)
fprintf(vlog_out, "[%d:%d]", msb, lsb);
else
fprintf(vlog_out, "[%d]", lsb);
}
/* /*
* Look for a single driver behind an LPM that passes strength information * Look for a single driver behind an LPM that passes strength information
* and get the real drive information from it. * and get the real drive information from it.
@ -758,7 +779,7 @@ static ivl_signal_t find_output_signal(ivl_scope_t scope, ivl_nexus_t nex,
} }
static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex,
int*base, unsigned*array_word) int*msb, int*lsb, unsigned*array_word)
{ {
unsigned idx, count = ivl_nexus_ptrs(nex); unsigned idx, count = ivl_nexus_ptrs(nex);
ivl_lpm_t lpm = 0; ivl_lpm_t lpm = 0;
@ -767,10 +788,16 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex,
ivl_signal_t sig; ivl_signal_t sig;
/* Look for a signal in the local scope first. */ /* Look for a signal in the local scope first. */
sig = find_local_signal(scope, nex, array_word); sig = find_local_signal(scope, nex, array_word);
if (sig) return sig; if (sig) {
get_sig_msb_lsb(sig, msb, lsb);
return sig;
}
/* Now look for an output signal driving into the local scope. */ /* Now look for an output signal driving into the local scope. */
sig = find_output_signal(scope, nex, array_word); sig = find_output_signal(scope, nex, array_word);
if (sig) return sig; if (sig) {
get_sig_msb_lsb(sig, msb, lsb);
return sig;
}
/* Now scan the nexus looking for a driver. */ /* Now scan the nexus looking for a driver. */
for (idx = 0; idx < count; idx += 1) { for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
@ -785,11 +812,18 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex,
/* The real signal could be hidden behind a select. */ /* The real signal could be hidden behind a select. */
if (ivl_lpm_type(t_lpm) == IVL_LPM_PART_VP) { if (ivl_lpm_type(t_lpm) == IVL_LPM_PART_VP) {
t_sig = nexus_is_signal(scope, ivl_lpm_data(t_lpm, 0), t_sig = nexus_is_signal(scope, ivl_lpm_data(t_lpm, 0),
base, array_word); msb, lsb, array_word);
} }
if (t_sig) *base += ivl_lpm_base(t_lpm); if (t_sig) {
else lpm = t_lpm; if (*msb >= *lsb) {
*lsb += ivl_lpm_base(t_lpm);
*msb = *lsb + (int)ivl_lpm_width(t_lpm) - 1;
} else {
*lsb -= ivl_lpm_base(t_lpm);
*msb = *lsb - (int)ivl_lpm_width(t_lpm) + 1;
}
} else lpm = t_lpm;
} }
if (t_net_const) { if (t_net_const) {
assert(! net_const); assert(! net_const);
@ -802,7 +836,7 @@ static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex,
assert(ivl_logic_pins(t_nlogic) == 2); assert(ivl_logic_pins(t_nlogic) == 2);
t_sig = nexus_is_signal(scope, t_sig = nexus_is_signal(scope,
ivl_logic_pin(t_nlogic, 1), ivl_logic_pin(t_nlogic, 1),
base, array_word); msb, lsb, array_word);
} else nlogic = t_nlogic; } else nlogic = t_nlogic;
} }
if (t_sig) { if (t_sig) {
@ -821,9 +855,9 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm,
unsigned width = ivl_lpm_width(lpm); unsigned width = ivl_lpm_width(lpm);
unsigned array_word = 0; unsigned array_word = 0;
int base = ivl_lpm_base(lpm); int base = ivl_lpm_base(lpm);
int msb, lsb; int msb = 0, lsb = 0;
ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0),
&base, &array_word); &msb, &lsb, &array_word);
if (sign_extend && !allow_signed) { if (sign_extend && !allow_signed) {
fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not "
@ -854,9 +888,9 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm,
fprintf(vlog_out, "[%d]", array_idx); fprintf(vlog_out, "[%d]", array_idx);
} }
get_sig_msb_lsb(sig, &msb, &lsb);
if (sign_extend) { if (sign_extend) {
assert(base != lsb); assert(base != lsb);
// HERE: This looks wrong.
if (msb >= lsb) base += lsb; if (msb >= lsb) base += lsb;
else base = lsb - base; else base = lsb - base;
fprintf(vlog_out, " >>> %d)", base); fprintf(vlog_out, " >>> %d)", base);
@ -880,7 +914,7 @@ static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm,
} }
} else { } else {
/* Skip a select of the entire bit. */ /* Skip a select of the entire bit. */
if ((msb == lsb) && (lsb == base)) return; if ((msb == lsb) && (base == 0)) return;
fprintf(vlog_out, "["); fprintf(vlog_out, "[");
if (msb >= lsb) base += lsb; if (msb >= lsb) base += lsb;
else base = lsb - base; else base = lsb - base;
@ -926,6 +960,103 @@ static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm)
} }
} }
static void emit_mux_select_bit(ivl_scope_t scope, ivl_lpm_t lpm, unsigned bit)
{
unsigned array_word = 0;
int msb = 0, lsb = 0;
ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_select(lpm),
&msb, &lsb, &array_word);
assert(sig);
emit_scope_call_path(scope, ivl_signal_scope(sig));
emit_id(ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig)) {
int array_idx = (int) array_word + ivl_signal_array_base(sig);
fprintf(vlog_out, "[%d]", array_idx);
}
if (msb >= lsb) {
fprintf(vlog_out, "[%d]", lsb + (int)bit);
} else {
fprintf(vlog_out, "[%d]", lsb - (int)bit);
}
}
static void emit_lpm_mux(ivl_scope_t scope, ivl_lpm_t lpm,
unsigned sel_bit, unsigned offset)
{
assert(sel_bit < sizeof(unsigned)*8);
fprintf(vlog_out, "(");
emit_mux_select_bit(scope, lpm, sel_bit);
fprintf(vlog_out, " ? ");
if (sel_bit > 0) {
emit_lpm_mux(scope, lpm, sel_bit - 1, offset + (1U << sel_bit));
fprintf(vlog_out, " : ");
emit_lpm_mux(scope, lpm, sel_bit - 1, offset);
} else {
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 1), 0, 0);
fprintf(vlog_out, " : ");
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 0), 0, 0);
}
fprintf(vlog_out, ")");
}
static void emit_lpm_substitute(ivl_scope_t scope, ivl_lpm_t lpm)
{
unsigned array_word = 0;
int msb = 0, lsb = 0;
unsigned base = ivl_lpm_base(lpm);
unsigned width;
int psb;
/* Find the wider signal. */
ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0),
&msb, &lsb, &array_word);
assert(sig);
// Get the width of the part being substituted.
width = get_nexus_width(ivl_lpm_data(lpm, 1));
fprintf(vlog_out, "{");
if (msb >= lsb) {
psb = lsb + base + width;
} else {
psb = lsb - base - width;
}
if (psb <= msb) {
emit_scope_call_path(scope, ivl_signal_scope(sig));
emit_id(ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig)) {
int array_idx = (int) array_word + ivl_signal_array_base(sig);
fprintf(vlog_out, "[%d]", array_idx);
}
emit_part_selector(msb, psb);
fprintf(vlog_out, ", ");
}
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0);
if (msb >= lsb) {
psb = lsb + base - 1;
} else {
psb = lsb - base + 1;
}
if (psb >= lsb) {
fprintf(vlog_out, ", ");
emit_scope_call_path(scope, ivl_signal_scope(sig));
emit_id(ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig)) {
int array_idx = (int) array_word + ivl_signal_array_base(sig);
fprintf(vlog_out, "[%d]", array_idx);
}
emit_part_selector(psb, lsb);
}
fprintf(vlog_out, "}");
}
static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
unsigned sign_extend) unsigned sign_extend)
{ {
@ -1087,6 +1218,9 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
fprintf(vlog_out, ")"); fprintf(vlog_out, ")");
break; break;
case IVL_LPM_MUX: case IVL_LPM_MUX:
if (ivl_lpm_selects(lpm) > 1) {
emit_lpm_mux(scope, lpm, ivl_lpm_selects(lpm) - 1, 0);
} else {
fprintf(vlog_out, "("); fprintf(vlog_out, "(");
emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0);
fprintf(vlog_out, " ? "); fprintf(vlog_out, " ? ");
@ -1094,6 +1228,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
fprintf(vlog_out, " : "); fprintf(vlog_out, " : ");
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0);
fprintf(vlog_out, ")"); fprintf(vlog_out, ")");
}
break; break;
case IVL_LPM_PART_PV: case IVL_LPM_PART_PV:
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0);
@ -1188,11 +1323,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
fprintf(vlog_out, ")"); fprintf(vlog_out, ")");
break; break;
case IVL_LPM_SUBSTITUTE: case IVL_LPM_SUBSTITUTE:
fprintf(vlog_out, "<unknown>"); emit_lpm_substitute(scope, lpm);
fprintf(stderr, "%s:%u: vlog95 sorry: Substitute LPMs are "
"not currently translated.\n",
ivl_lpm_file(lpm), ivl_lpm_lineno(lpm));
vlog_errors += 1;
break; break;
case IVL_LPM_UFUNC: case IVL_LPM_UFUNC:
emit_scope_path(scope, ivl_lpm_define(lpm)); emit_scope_path(scope, ivl_lpm_define(lpm));

View File

@ -1,5 +1,7 @@
functor:synth2 functor:synth2
functor:synth functor:synth
functor:syn-rules functor:syn-rules
functor:synthsplit
functor:nodangle
flag:DLL=vlog95.tgt flag:DLL=vlog95.tgt
flag:DISABLE_CONCATZ_GENERATION=true flag:DISABLE_CONCATZ_GENERATION=true