Skip to content

Commit

Permalink
src: add run-time randomizer and implement PSL union with it.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ondrej Ille authored and ondrej.ille committed Jan 27, 2025
1 parent df373f9 commit 25fa166
Show file tree
Hide file tree
Showing 22 changed files with 234 additions and 15 deletions.
7 changes: 7 additions & 0 deletions nvc.1
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,13 @@ are included in or excluded from the waveform dump. See section
.Sx SELECTING SIGNALS
for details on how to select particular signals. These options can be
given multiple times.
.It Fl \-seed Ns = Ns Ar S
Seed for simulator randomization. The
.Ar S
shall be an integer between 0 and 4294967295. If
.Fl \-seed
is not used, NVC will pick random
seed based on system time.
.\" --shuffle
.It Fl \-shuffle
Run processes in random order. The VHDL standard does not specify the
Expand Down
6 changes: 6 additions & 0 deletions src/jit/jit-exits.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,12 @@ void __nvc_do_exit(jit_exit_t which, jit_anchor_t *anchor, jit_scalar_t *args,
}
break;

case JIT_EXIT_GET_RANDOM:
{
x_get_random(args);
}
break;

default:
fatal_trace("unhandled exit %s", jit_exit_name(which));
}
Expand Down
1 change: 1 addition & 0 deletions src/jit/jit-exits.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,6 @@ void *x_port_conversion(const ffi_closure_t *driving,
void x_convert_in(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count);
void x_convert_out(void *ptr, sig_shared_t *ss, uint32_t offset, int32_t count);
void x_bind_external(tree_t where, jit_handle_t scope, jit_scalar_t *result);
void x_get_random(jit_scalar_t *result);

#endif // _JIT_EXITS_H
10 changes: 10 additions & 0 deletions src/jit/jit-irgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -3622,6 +3622,13 @@ static void irgen_op_bind_external(jit_irgen_t *g, int op)
j_recv(g, i);
}

static void irgen_op_get_random(jit_irgen_t *g, int op)
{
macro_exit(g, JIT_EXIT_GET_RANDOM);

g->map[vcode_get_result(op)] = j_recv(g, 0);
}

static void irgen_block(jit_irgen_t *g, vcode_block_t block)
{
vcode_select_block(block);
Expand Down Expand Up @@ -4031,6 +4038,9 @@ static void irgen_block(jit_irgen_t *g, vcode_block_t block)
case VCODE_OP_BIND_EXTERNAL:
irgen_op_bind_external(g, i);
break;
case VCODE_OP_GET_RANDOM:
irgen_op_get_random(g, i);
break;
default:
fatal_trace("cannot generate JIT IR for vcode op %s",
vcode_op_string(op));
Expand Down
1 change: 1 addition & 0 deletions src/jit/jit-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ typedef enum {
JIT_EXIT_BIND_EXTERNAL,
JIT_EXIT_SYSCALL,
JIT_EXIT_PUT_CONVERSION,
JIT_EXIT_GET_RANDOM
} jit_exit_t;

typedef uint16_t jit_reg_t;
Expand Down
12 changes: 12 additions & 0 deletions src/nvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ static int run_cmd(int argc, char **argv, cmd_state_t *state)
{ "vhpi-trace", no_argument, 0, 'T' },
{ "gtkw", optional_argument, 0, 'g' },
{ "shuffle", no_argument, 0, 'H' },
{ "seed", required_argument, 0, 'r' },
{ 0, 0, 0, 0 }
};

Expand All @@ -727,6 +728,11 @@ static int run_cmd(int argc, char **argv, cmd_state_t *state)
const int next_cmd = scan_cmd(2, argc, argv);

int c, index = 0;

time_t now = time(NULL);
srand(now);
uint32_t seed = rand();

const char *spec = ":w::l:gi";
while ((c = getopt_long(next_cmd, argv, spec, long_options, &index)) != -1) {
switch (c) {
Expand Down Expand Up @@ -806,6 +812,11 @@ static int run_cmd(int argc, char **argv, cmd_state_t *state)
"as non-deterministic behaviour");
opt_set_int(OPT_SHUFFLE_PROCS, 1);
break;
case 'r':
// Hash the user seed to better preseed the model TRNG
seed = parse_int(optarg) + 1;
seed *= UINT32_C(2654435761);
break;
default:
abort();
}
Expand Down Expand Up @@ -896,6 +907,7 @@ static int run_cmd(int argc, char **argv, cmd_state_t *state)

set_ctrl_c_handler(ctrl_c_handler, model);

model_set_seed(model, seed);
model_reset(model);

if (dumper != NULL)
Expand Down
33 changes: 24 additions & 9 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -11498,6 +11498,18 @@ static psl_node_t p_psl_value_set(void)
return p;
}

static psl_node_t p_psl_union(psl_node_t left, psl_type_t type)
{
consume(tUNION);

psl_node_t un = psl_new(P_UNION);
psl_add_operand(un, left);
psl_add_operand(un, p_hdl_expression(NULL, type));
psl_set_loc(un, CURRENT_LOC);

return un;
}

static psl_node_t p_hdl_expression(tree_t head, psl_type_t type)
{
BEGIN("PSL HDL expression");
Expand All @@ -11518,8 +11530,11 @@ static psl_node_t p_hdl_expression(tree_t head, psl_type_t type)
psl_set_tree(p, expr);
psl_set_loc(p, tree_loc(expr));
psl_set_type(p, type);

psl_set_loc(p, CURRENT_LOC);

if (scan(tUNION))
return p_psl_union(p, type);

return p;
}

Expand All @@ -11532,20 +11547,16 @@ static psl_node_t p_psl_or_hdl_expression(void)

psl_node_t head = p_hdl_expression(NULL, PSL_TYPE_BOOLEAN);

if (optional(tUNION)) {
psl_node_t new = psl_new(P_UNION);
psl_add_operand(new, head);
psl_add_operand(new, p_psl_or_hdl_expression());
psl_set_loc(new, CURRENT_LOC);
return new;
}
if (scan(tUNION))
return p_psl_union(head, PSL_TYPE_BOOLEAN);

return head;
}

static psl_node_t p_psl_boolean(tree_t head)
{
// HDL_or_PSL_Expression
// HDL_Expression | PSL_Expression | Built_In_Function_Call
// | Union_Expression

BEGIN_WITH_HEAD("PSL Boolean", head);

Expand All @@ -11570,6 +11581,10 @@ static psl_node_t p_psl_boolean(tree_t head)
psl_set_loc(p, CURRENT_LOC);
return p;
}
case tUNION:
{
return p_psl_union(p, PSL_TYPE_BOOLEAN);
}

default:
return p;
Expand Down
1 change: 1 addition & 0 deletions src/psl/psl-fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ static fsm_state_t *build_node(psl_fsm_t *fsm, fsm_state_t *state, psl_node_t p)
fsm->kind = FSM_ALWAYS;
return build_node(fsm, state, psl_value(p));
case P_HDL_EXPR:
case P_UNION:
{
fsm_state_t *new = add_state(fsm, p);
add_edge(fsm, state, new, EDGE_EPSILON, p);
Expand Down
38 changes: 35 additions & 3 deletions src/psl/psl-lower.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#define PSL_BLOCK_ABORT 2
#define PSL_BLOCK_PREV 3

static vcode_reg_t psl_lower_expr(lower_unit_t *lu, psl_node_t p);


static void psl_wait_cb(tree_t t, void *ctx)
{
lower_unit_t *lu = ctx;
Expand All @@ -58,6 +61,35 @@ static vcode_reg_t psl_lower_boolean(lower_unit_t *lu, psl_node_t p)
return test_reg;
}

static vcode_reg_t psl_lower_union(lower_unit_t *lu, psl_node_t p)
{
vcode_reg_t lhs = psl_lower_expr(lu, psl_operand(p, 0));
vcode_reg_t rhs = psl_lower_expr(lu, psl_operand(p, 1));

vcode_reg_t rnd = emit_get_random();

vcode_type_t vint = vtype_int(INT64_MIN, INT64_MAX);
vcode_reg_t mod = emit_mod(rnd, emit_const(vint, 2));
vcode_reg_t sel = emit_cmp(VCODE_CMP_EQ, mod, emit_const(vint, 0));

vcode_reg_t res = emit_select(sel, lhs, rhs);

return res;
}

static vcode_reg_t psl_lower_expr(lower_unit_t *lu, psl_node_t p)
{
switch (psl_kind(p)) {
case P_HDL_EXPR:
return psl_lower_boolean(lu, p);
case P_UNION:
return psl_lower_union(lu, p);
default:
fatal_at(psl_loc(p), "cannot lower PSL expression kind %s",
psl_kind_str(psl_kind(p)));
}
}

static vcode_reg_t psl_debug_locus(psl_node_t p)
{
return emit_debug_locus(psl_to_object(p));
Expand All @@ -72,7 +104,7 @@ static vcode_reg_t psl_lower_guard(lower_unit_t *lu, psl_guard_t g)
{
switch (psl_guard_kind(g)) {
case GUARD_EXPR:
return psl_lower_boolean(lu, psl_guard_expr(g));
return psl_lower_expr(lu, psl_guard_expr(g));
case GUARD_BINOP:
{
const guard_binop_t *bop = psl_guard_binop(g);
Expand All @@ -89,7 +121,7 @@ static vcode_reg_t psl_lower_guard(lower_unit_t *lu, psl_guard_t g)
}
}
case GUARD_NOT:
return emit_not(psl_lower_boolean(lu, psl_guard_expr(g)));
return emit_not(psl_lower_expr(lu, psl_guard_expr(g)));
default:
should_not_reach_here();
}
Expand Down Expand Up @@ -258,7 +290,7 @@ static vcode_reg_t psl_lower_async_abort(unit_registry_t *ur,
lower_unit_t *lu = lower_unit_new(ur, parent, vu, NULL, NULL);
unit_registry_put(ur, lu);

vcode_reg_t result_reg = psl_lower_boolean(lu, hdl_expr);
vcode_reg_t result_reg = psl_lower_expr(lu, hdl_expr);
emit_return(result_reg);

unit_registry_finalise(ur, lu);
Expand Down
10 changes: 10 additions & 0 deletions src/psl/psl-sem.c
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,13 @@ static void psl_check_builtin_fcall(psl_node_t p, nametab_t *tab)
psl_check(psl_operand(p, i), tab);
}

static void psl_check_union(psl_node_t p, nametab_t *tab)
{
const int nparams = psl_operands(p);
for (int i = 0; i < nparams; i++)
psl_check(psl_operand(p, i), tab);
}

void psl_check(psl_node_t p, nametab_t *tab)
{
switch (psl_kind(p)) {
Expand Down Expand Up @@ -554,6 +561,9 @@ void psl_check(psl_node_t p, nametab_t *tab)
case P_BUILTIN_FCALL:
psl_check_builtin_fcall(p, tab);
break;
case P_UNION:
psl_check_union(p, tab);
break;
default:
fatal_trace("cannot check PSL kind %s", psl_kind_str(psl_kind(p)));
}
Expand Down
3 changes: 2 additions & 1 deletion src/rt/Makemodule.am
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ lib_libnvc_a_SOURCES += \
src/rt/assert.h \
src/rt/ename.c \
src/rt/copy.h \
src/rt/copy.c
src/rt/copy.c \
src/rt/random.c

if ENABLE_TCL
lib_libnvc_a_SOURCES += \
Expand Down
23 changes: 23 additions & 0 deletions src/rt/model.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ typedef struct _rt_model {
bool shuffle;
bool liveness;
rt_trigger_t *triggertab[TRIGGER_TAB_SIZE];
rt_randomizer_t *random;
} rt_model_t;

#define FMT_VALUES_SZ 128
Expand Down Expand Up @@ -579,6 +580,7 @@ rt_model_t *model_new(tree_t top, jit_t *jit)
m->eventq_heap = heap_new(512);
m->res_memo = ihash_new(128);
m->shuffle = opt_get_int(OPT_SHUFFLE_PROCS);
m->random = static_alloc(m, sizeof(rt_randomizer_t));

m->driving_heap = heap_new(64);
m->effective_heap = heap_new(64);
Expand Down Expand Up @@ -2396,6 +2398,8 @@ void model_reset(rt_model_t *m)
reset_coverage(m);
reset_scope(m, m->root);

m->random->state = m->random->seed;

if (m->force_stop)
return; // Error in intialisation

Expand Down Expand Up @@ -3590,6 +3594,25 @@ void model_set_timeout_cb(rt_model_t *m, uint64_t when, rt_event_fn_t fn,
heap_insert(m->eventq_heap, when, e);
}

void model_set_seed(rt_model_t *m, uint32_t seed)
{
m->random->seed = (((uint64_t)seed) << 32) | seed;
}

uint64_t model_get_random_number(rt_model_t *m)
{
uint64_t lfsr = m->random->state;
uint64_t fb = ~(((lfsr >> 63) & 0x1) ^
((lfsr >> 62) & 0x1) ^
((lfsr >> 60) & 0x1) ^
((lfsr >> 59) & 0x1));
uint64_t rnd = (lfsr << 1) | (fb & 0x1);

m->random->state = rnd;

return rnd;
}

rt_watch_t *watch_new(rt_model_t *m, sig_event_fn_t fn, void *user,
watch_kind_t kind, unsigned slots)
{
Expand Down
3 changes: 3 additions & 0 deletions src/rt/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ void model_stop(rt_model_t *m);
void model_interrupt(rt_model_t *m);
int model_exit_status(rt_model_t *m);

void model_set_seed(rt_model_t *m, uint32_t seed);
uint64_t model_get_random_number(rt_model_t *m);

rt_watch_t *watch_new(rt_model_t *m, sig_event_fn_t fn, void *user,
watch_kind_t kind, unsigned slots);
void watch_free(rt_model_t *m, rt_watch_t *w);
Expand Down
34 changes: 34 additions & 0 deletions src/rt/random.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright (C) 2024 Nick Gasson
//
// 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 3 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, see <http://www.gnu.org/licenses/>.
//

#include "stdlib.h"
#include "jit/jit.h"
#include "rt/model.h"

void x_get_random(jit_scalar_t *result)
{
rt_model_t *m = get_model_or_null();

// TODO: Would need to be handled when randomizer is called from
// elaboration (e.g. vlog param initialied by $random)
if (m == NULL)
return;

int64_t rnd_val = model_get_random_number(m);

result[0].integer = rnd_val;
}
2 changes: 1 addition & 1 deletion src/rt/rt.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

#include <stdint.h>

#define RT_ABI_VERSION 29
#define RT_ABI_VERSION 30
#define RT_ALIGN_MASK 0x7
#define RT_MULTITHREADED 0

Expand Down
Loading

0 comments on commit 25fa166

Please sign in to comment.