New command %gen.

This commit is contained in:
Alan Mishchenko 2025-11-09 11:28:25 -08:00
parent 3109172462
commit 6cab944535
4 changed files with 649 additions and 0 deletions

View File

@ -32,6 +32,7 @@ ABC_NAMESPACE_IMPL_START
static int Abc_CommandReadWlc ( Abc_Frame_t * pAbc, int argc, char ** argv );
static int Abc_CommandWriteWlc ( Abc_Frame_t * pAbc, int argc, char ** argv );
static int Abc_CommandGenWlc ( Abc_Frame_t * pAbc, int argc, char ** argv );
static int Abc_CommandPs ( Abc_Frame_t * pAbc, int argc, char ** argv );
static int Abc_CommandCone ( Abc_Frame_t * pAbc, int argc, char ** argv );
static int Abc_CommandAbs ( Abc_Frame_t * pAbc, int argc, char ** argv );
@ -79,6 +80,7 @@ void Wlc_Init( Abc_Frame_t * pAbc )
{
Cmd_CommandAdd( pAbc, "Word level", "%read", Abc_CommandReadWlc, 0 );
Cmd_CommandAdd( pAbc, "Word level", "%write", Abc_CommandWriteWlc, 0 );
Cmd_CommandAdd( pAbc, "Word level", "%gen", Abc_CommandGenWlc, 0 );
Cmd_CommandAdd( pAbc, "Word level", "%ps", Abc_CommandPs, 0 );
Cmd_CommandAdd( pAbc, "Word level", "%cone", Abc_CommandCone, 0 );
Cmd_CommandAdd( pAbc, "Word level", "%abs", Abc_CommandAbs, 0 );
@ -305,6 +307,119 @@ usage:
return 1;
}
/**Function********************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
******************************************************************************/
int Abc_CommandGenWlc( Abc_Frame_t * pAbc, int argc, char ** argv )
{
extern int miniver_translate(const char *input, char *out, size_t cap);
char * pFileName = NULL;
int c, fVerbose = 0;
Extra_UtilGetoptReset();
while ( ( c = Extra_UtilGetopt( argc, argv, "Fvh" ) ) != EOF )
{
switch ( c )
{
case 'F':
if ( globalUtilOptind >= argc )
{
Abc_Print( -1, "Command line switch \"-L\" should be followed by a file name.\n" );
goto usage;
}
pFileName = argv[globalUtilOptind];
globalUtilOptind++;
break;
case 'v':
fVerbose ^= 1;
break;
case 'h':
goto usage;
default:
goto usage;
}
}
if ( argc != globalUtilOptind + 1 ) {
printf( "The input string is not given on the command line.\n" );
goto usage;
}
else {
int Size = 10000;
char * pStr = argv[globalUtilOptind];
char * pOutStr = ABC_CALLOC( char, Size+1 );
int RetValue = miniver_translate( pStr, pOutStr, Size );
if ( !RetValue ) {
if ( fVerbose )
printf( "Entered Verilog design:\n%s", pOutStr );
if ( pFileName ) {
FILE * pFile = fopen( pFileName, "wb" );
if ( pFile == NULL )
printf( "Cannot open output file \"%s\".\n", pFileName );
else {
fprintf( pFile, "// Design generated from mini-Verilog string: %s\n\n%s\n", pStr, pOutStr );
fclose( pFile );
printf( "Dumped the design generated from mini-Verilog string \"%s\" into file \"%s\".\n", pStr, pFileName );
}
}
else {
Wlc_Ntk_t * pNtk = Wlc_ReadVer( NULL, pOutStr, 0 );
if ( pNtk ) {
Wlc_AbcUpdateNtk( pAbc, pNtk );
//printf( "Read current design using %%read <file.v>\n" );
}
else {
printf( "The following design in Verilog, which was generated from string \"%s\",\n", pStr );
printf( "cannot be read into ABC due to the known limitations of command \"%%read\".\n" );
printf( "Please try the following \"%%gen -F <file.v> <string>; %%yosys -b <file.v>; &ps\".\n" );
printf( "Generated design:\n%s\n", pOutStr );
}
}
}
ABC_FREE( pOutStr );
}
return 0;
usage:
Abc_Print( -2, "\nusage: %%gen [-F file] [-vh] \"<mini_verilog_string>\"\n" );
Abc_Print( -2, "\t generates the design from a mini-Verilog string\n" );
Abc_Print( -2, "\t-F file : optional file name to save the design in standard Verilog [default = unused]\n" );
Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" );
Abc_Print( -2, "\t-h : print the command usage\n");
Abc_Print( -2, "\n" );
Abc_Print( -2, "A mini-Verilog design is a single string. Any spaces/tabs/newlines are ignored.\n" );
Abc_Print( -2, "The string is split into clauses by semicolons ';'.\n" );
Abc_Print( -2, "\n" );
Abc_Print( -2, "Clause kinds (first character):\n" );
Abc_Print( -2, " - 'm<name>' : module name (appear once)\n" );
Abc_Print( -2, " - '{i|o|w}[s]<number><id_list>' : input/output/wire declarations\n" );
Abc_Print( -2, " where optional 's' = signed, <number> = bit-width (>0), <id_list> = id[,id]*\n" );
Abc_Print( -2, " Examples: i4a,b -> input [3:0] a, b;\n" );
Abc_Print( -2, " is8x,y -> input signed [7:0] x, y;\n" );
Abc_Print( -2, " w1t -> wire t; (width 1 prints without [0:0])\n" );
Abc_Print( -2, " - '{o|w}[s]<number><id>=<expr>' : declaration with assignment\n" );
Abc_Print( -2, " Examples: o8z=a*b -> output [7:0] z; assign z = a*b;\n" );
Abc_Print( -2, " w4t=a+b -> wire [3:0] t; assign t = a+b;\n" );
Abc_Print( -2, "\n" );
Abc_Print( -2, "Default names:\n" );
Abc_Print( -2, " - Outputs: if an 'o...' assignment omits a name (e.g., 'o8=<expr>'), the output name defaults to 'o'.\n" );
Abc_Print( -2, " - Inputs: if an 'i...' clause omits names (e.g., 'i4'), a single input is declared with an\n" );
Abc_Print( -2, " auto-generated unsigned name 'a', then 'b', then 'c', ... in the order of appearance (skipping\n" );
Abc_Print( -2, " names already used). This allows for specifying a 4-bit multiplier 'mul' as: 'mmul;i4;i4;o8=a*b'.\n" );
Abc_Print( -2, "\n" );
Abc_Print( -2, "Notes:\n" );
Abc_Print( -2, " * Only a single, non-hierarchical, combinational module is supported.\n" );
Abc_Print( -2, " * Whitespace anywhere is ignored before parsing (handled internally).\n" );
Abc_Print( -2, " * RHS expressions are passed through verbatim (must be in valid Verilog).\n" );
return 1;
}
/**Function********************************************************************
Synopsis []

View File

@ -6,6 +6,7 @@ SRC += src/misc/util/utilBridge.c \
src/misc/util/utilFile.c \
src/misc/util/utilIsop.c \
src/misc/util/utilLinear.c \
src/misc/util/utilMiniver.c \
src/misc/util/utilNam.c \
src/misc/util/utilPrefix.cpp \
src/misc/util/utilPth.c \

523
src/misc/util/utilMiniver.c Normal file
View File

@ -0,0 +1,523 @@
/**CFile****************************************************************
FileName [utilMiniver.c]
SystemName [ABC: Logic synthesis and verification system.]
PackageName [Handling counter-examples.]
Synopsis [Handling counter-examples.]
Author [Alan Mishchenko]
Affiliation [UC Berkeley]
Date [Ver. 1.0. Started - June 20, 2005.]
Revision [$Id: utilMiniver.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]
***********************************************************************/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "misc/util/abc_global.h"
ABC_NAMESPACE_IMPL_START
////////////////////////////////////////////////////////////////////////
/// DECLARATIONS ///
////////////////////////////////////////////////////////////////////////
#define MINIVER_LIBRARY_ONLY
#define MAXSIG 1024
#define MAXSTR 65536
////////////////////////////////////////////////////////////////////////
/// FUNCTION DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
typedef struct {
char name[128];
int width;
int is_signed;
} decl_t;
typedef struct {
char lhs[128];
char rhs[4096];
} asn_t;
// Parsing/collection context (encapsulates all state)
typedef struct {
char modname[128];
decl_t inputs[MAXSIG]; int ni;
decl_t outputs[MAXSIG]; int no;
decl_t wires[MAXSIG]; int nw;
asn_t assigns[MAXSIG]; int na;
int next_auto_in; // next index for auto-generated input names ('a' + idx)
} mv_ctx;
// -----------------------------------------------------------------------------
// Utilities
// -----------------------------------------------------------------------------
// Remove all ASCII whitespace from 's' into 'out'.
static void strip_ws(const char *s, char *out) {
while (*s) {
if (!isspace((unsigned char)*s)) {
*out++ = *s;
}
++s;
}
*out = 0;
}
static int is_ident_start(char c) {
return isalpha((unsigned char)c) || c == '_' || c == '$';
}
static int is_ident_char(char c) {
return isalnum((unsigned char)c) || c == '_' || c == '$';
}
// Parse a non-negative decimal integer from 's'; store value in *v and chars consumed in *nch.
static int parse_uint(const char *s, int *v, int *nch) {
int i = 0;
int d = 0;
if (!isdigit((unsigned char)s[0])) return 0;
while (isdigit((unsigned char)s[i])) {
d = d * 10 + (s[i] - '0');
++i;
}
*v = d;
*nch = i;
return 1;
}
// Check if a name is already used in any declaration.
static int name_exists(mv_ctx *ctx, const char *name) {
for (int i = 0; i < ctx->ni; ++i) if (!strcmp(ctx->inputs[i].name, name)) return 1;
for (int i = 0; i < ctx->no; ++i) if (!strcmp(ctx->outputs[i].name, name)) return 1;
for (int i = 0; i < ctx->nw; ++i) if (!strcmp(ctx->wires[i].name, name)) return 1;
return 0;
}
// Generate next auto input name: 'a','b','c',... then 'a0','a1',... if 26 exhausted.
static void gen_auto_in_name(mv_ctx *ctx, char *out, size_t out_sz) {
// try single letters first
for (; ctx->next_auto_in < 26; ++ctx->next_auto_in) {
char cand[3] = {(char)('a' + ctx->next_auto_in), 0, 0};
if (!name_exists(ctx, cand)) { strncpy(out, cand, out_sz-1); out[out_sz-1]=0; ++ctx->next_auto_in; return; }
}
// fallback to aN form
for (int n = 0;; ++n) {
char cand[32];
snprintf(cand, sizeof(cand), "a%d", n);
if (!name_exists(ctx, cand)) { strncpy(out, cand, out_sz-1); out[out_sz-1]=0; return; }
}
}
// Add (or deduplicate) declaration into an array.
static void add_decl(decl_t *arr, int *cnt, const char *name, int w, int sg) {
for (int k = 0; k < *cnt; ++k) {
if (!strcmp(arr[k].name, name)) return;
}
strncpy(arr[*cnt].name, name, sizeof(arr[*cnt].name) - 1);
arr[*cnt].width = w;
arr[*cnt].is_signed = sg;
++(*cnt);
}
// Record an assignment; returns non-zero on overflow.
static int add_asn(mv_ctx *ctx, const char *lhs, const char *rhs) {
if (ctx->na >= MAXSIG) {
printf("Too many assignments.\n");
return 1;
}
strncpy(ctx->assigns[ctx->na].lhs, lhs, sizeof(ctx->assigns[ctx->na].lhs) - 1);
strncpy(ctx->assigns[ctx->na].rhs, rhs, sizeof(ctx->assigns[ctx->na].rhs) - 1);
++ctx->na;
return 0;
}
// Append formatted text to user-provided buffer with capacity checks.
// On insufficient capacity, print message and return 1; otherwise 0.
static int out_cat(char **p, size_t *left, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int need = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (need < 0 || (size_t)need >= *left) {
printf("Output exceeds buffer capacity.\n");
return 1;
}
va_start(ap, fmt);
int wrote = vsnprintf(*p, *left, fmt, ap);
va_end(ap);
*p += wrote;
*left -= (size_t)wrote;
return 0;
}
// Emit declarations into the output buffer.
static int print_decl_to_buf(char **p, size_t *left, const char *kw, decl_t *arr, int n) {
for (int i = 0; i < n; ++i) {
int w = arr[i].width;
int sg = arr[i].is_signed;
const char *id = arr[i].name;
if (w == 1) {
if (out_cat(p, left, " %s %s%s;\n", kw, sg ? "signed " : "", id)) return 1;
} else {
if (out_cat(p, left, " %s %s[%d:0] %s;\n", kw, sg ? "signed " : "", w - 1, id)) return 1;
}
}
return 0;
}
// Add spaces around every alphanumeric/underscore sequence for readability.
// Example: "a*b+16'b0" -> " a * b + 16 ' b0 "
static void format_rhs_readable(const char *in, char *out, size_t cap) {
size_t o = 0;
for (size_t i = 0; in[i]; ) {
unsigned char c = (unsigned char)in[i];
if (isalnum(c) || c == '_') {
// start of a token
if (o + 1 >= cap) { if (o) out[o-1] = 0; else if (cap) out[0] = 0; return; }
out[o++] = ' ';
// copy token
size_t j = i;
while (in[j] && (isalnum((unsigned char)in[j]) || in[j] == '_')) {
if (o + 1 >= cap) { out[o-1] = 0; return; }
out[o++] = in[j++];
}
if (o + 1 >= cap) { out[o-1] = 0; return; }
out[o++] = ' ';
i = j;
} else {
if (o + 1 >= cap) { if (o) out[o-1] = 0; else if (cap) out[0] = 0; return; }
out[o++] = in[i++];
}
}
if (o < cap) out[o] = 0; else if (cap) out[cap-1] = 0;
}
// -----------------------------------------------------------------------------
// Parsing helpers that quote offending clauses
// -----------------------------------------------------------------------------
typedef int (*emit_fn)(mv_ctx *, const char *, int, int);
static int emit_in(mv_ctx *ctx, const char *id, int w, int sg) {
add_decl(ctx->inputs, &ctx->ni, id, w, sg);
return 0;
}
static int emit_wi(mv_ctx *ctx, const char *id, int w, int sg) {
add_decl(ctx->wires, &ctx->nw, id, w, sg);
return 0;
}
// Split an identifier list like "a,b,c" and emit each item. Quotes 'clause' in diagnostics.
static int split_ids(mv_ctx *ctx, const char *clause, const char *s, emit_fn emit, int w, int sg) {
char buf[128];
int j = 0;
for (int i = 0;; ++i) {
char c = s[i];
if (c == ',' || c == 0) {
if (j == 0) {
printf("Syntax error: empty identifier in expression \"%s\".\n", clause);
return 1;
}
buf[j] = 0;
if (emit(ctx, buf, w, sg)) return 1;
j = 0;
if (c == 0) break;
} else {
if (j >= 120) {
printf("Identifier too long in expression \"%s\".\n", clause);
return 1;
}
if (!((j ? is_ident_char(c) : is_ident_start(c)))) {
printf("Illegal identifier in expression \"%s\".\n", clause);
return 1;
}
buf[j++] = c;
}
}
return 0;
}
// Parse one clause (already whitespace-stripped). On error, prints a message including the clause.
static int parse_clause(mv_ctx *ctx, const char *q) {
if (!q || !*q) return 0;
if (q[0] == 'm') {
if (!is_ident_start(q[1])) {
printf("Syntax error after 'm' in expression \"%s\".\n", q);
return 1;
}
int i = 1;
while (q[i] && is_ident_char(q[i])) ++i;
if (q[i]) {
printf("Garbage after module name in expression \"%s\".\n", q);
return 1;
}
size_t n = (size_t)(i - 1);
if (n >= sizeof(ctx->modname)) n = sizeof(ctx->modname) - 1;
strncpy(ctx->modname, q + 1, n);
ctx->modname[n] = 0;
return 0;
}
if (q[0] != 'i' && q[0] != 'o' && q[0] != 'w') {
printf("Unknown clause start in expression \"%s\".\n", q);
return 1;
}
int kind = q[0];
int i = 1;
int sg = 0;
int w = 0;
int nconsumed = 0;
if (q[i] == 's') {
sg = 1;
++i;
}
if (!parse_uint(q + i, &w, &nconsumed)) {
printf("Width expected in expression \"%s\".\n", q);
return 1;
}
i += nconsumed;
if (w <= 0) {
printf("Non-positive width in expression \"%s\".\n", q);
return 1;
}
const char *eq = strchr(q + i, '=');
if (kind == 'i') {
// Support unnamed inputs: 'i4' -> auto-generate one input named a,b,c,...
if (!q[i]) {
char auto_name[32];
gen_auto_in_name(ctx, auto_name, sizeof(auto_name));
add_decl(ctx->inputs, &ctx->ni, auto_name, w, sg);
return 0;
}
if (eq) {
printf("Unexpected '=' in input clause in expression \"%s\".\n", q);
return 1;
}
return split_ids(ctx, q, q + i, emit_in, w, sg);
}
if (kind == 'w' && !eq) {
if (!q[i]) {
printf("Identifiers expected for wires in expression \"%s\".\n", q);
return 1;
}
return split_ids(ctx, q, q + i, emit_wi, w, sg);
}
if (!eq) {
printf("Assignment expected in expression \"%s\".\n", q);
return 1;
}
// Parse the LHS name for 'o' or 'w'. For 'o', allow omitted name (defaults to "o").
char lhs[128];
int lj = 0;
for (int k = i; k < (int)(eq - q); ++k) {
char c = q[k];
if (!lj && kind == 'o' && !is_ident_start(c)) {
lhs[0] = 'o';
lhs[1] = 0;
goto lhs_done;
}
if (!(lj ? is_ident_char(c) : is_ident_start(c))) {
printf("Illegal LHS identifier in expression \"%s\".\n", q);
return 1;
}
if (lj >= 120) {
printf("Identifier too long in expression \"%s\".\n", q);
return 1;
}
lhs[lj++] = c;
}
if (lj == 0) {
if (kind == 'o') {
lhs[0] = 'o';
lhs[1] = 0;
} else {
printf("Wire assignment needs a name in expression \"%s\".\n", q);
return 1;
}
} else {
lhs[lj] = 0;
}
lhs_done:;
const char *rhs = eq + 1;
if (!*rhs) {
printf("Empty RHS expression in expression \"%s\".\n", q);
return 1;
}
if (kind == 'o') add_decl(ctx->outputs, &ctx->no, lhs, w, sg);
if (kind == 'w') add_decl(ctx->wires, &ctx->nw, lhs, w, sg);
return add_asn(ctx, lhs, rhs);
}
// -----------------------------------------------------------------------------
// Public API
// -----------------------------------------------------------------------------
// Translate a raw mini-Verilog string 'input' into standard Verilog.
// The function strips whitespace internally, parses, and writes into 'out' (cap bytes).
// Returns 0 on success, 1 on error. Errors are printed (no exit()).
int miniver_translate(const char *input, char *out, size_t cap) {
if (!input || !out || cap == 0) {
printf("Invalid arguments.\n");
return 1;
}
// Step 1: strip whitespace (per spec).
char flat[MAXSTR];
strip_ws(input, flat);
if (!*flat) {
printf("Empty input.\n");
return 1;
}
// Step 2: parse clauses into a context.
mv_ctx *ctx = (mv_ctx*)calloc(1, sizeof(mv_ctx));
if (!ctx) {
printf("Out of memory.\n");
return 1;
}
strncpy(ctx->modname, "top", sizeof(ctx->modname) - 1);
ctx->next_auto_in = 0;
// Make a modifiable copy to split on semicolons.
char *buf = (char*)malloc(strlen(flat) + 1);
if (!buf) {
printf("Out of memory.\n");
free(ctx);
return 1;
}
strcpy(buf, flat);
char *s = buf;
while (*s) {
char *semi = strchr(s, ';');
if (semi) *semi = 0;
if (parse_clause(ctx, s)) {
free(buf);
free(ctx);
return 1;
}
if (!semi) break;
s = semi + 1;
}
free(buf);
if (!*ctx->modname) {
printf("Module name missing.\n");
free(ctx);
return 1;
}
if (ctx->no == 0) {
printf("At least one output with assignment is required.\n");
free(ctx);
return 1;
}
// Step 3: emit Verilog into caller buffer.
char *p = out;
size_t left = cap;
if (out_cat(&p, &left, "module %s (", ctx->modname)) {
free(ctx); return 1;
}
for (int i = 0; i < ctx->ni; ++i) {
if (out_cat(&p, &left, "%s%s", i ? ", " : "", ctx->inputs[i].name)) {
free(ctx); return 1;
}
}
for (int i = 0; i < ctx->no; ++i) {
if (out_cat(&p, &left, "%s%s", (ctx->ni || i) ? ", " : "", ctx->outputs[i].name)) {
free(ctx); return 1;
}
}
if (out_cat(&p, &left, ");\n")) {
free(ctx); return 1;
}
if (print_decl_to_buf(&p, &left, "input", ctx->inputs, ctx->ni)) { free(ctx); return 1; }
if (print_decl_to_buf(&p, &left, "output", ctx->outputs, ctx->no)) { free(ctx); return 1; }
if (print_decl_to_buf(&p, &left, "wire", ctx->wires, ctx->nw)) { free(ctx); return 1; }
for (int i = 0; i < ctx->na; ++i) {
char rhs_sp[8192];
format_rhs_readable(ctx->assigns[i].rhs, rhs_sp, sizeof(rhs_sp));
if (out_cat(&p, &left, " assign %s = %s;\n", ctx->assigns[i].lhs, rhs_sp)) {
free(ctx); return 1;
}
}
if (out_cat(&p, &left, "endmodule\n")) {
free(ctx); return 1;
}
*p = 0;
free(ctx);
return 0;
}
// -----------------------------------------------------------------------------
// Optional CLI wrapper (define MINIVER_LIBRARY_ONLY to omit main)
// -----------------------------------------------------------------------------
#ifndef MINIVER_LIBRARY_ONLY
int main(int argc, char **argv) {
char in[MAXSTR] = {0};
if (argc >= 2) {
size_t L = strlen(argv[1]);
if (L >= MAXSTR - 1) {
printf("Input too long.\n");
return 1;
}
strcpy(in, argv[1]);
} else {
size_t off = 0;
int c = 0;
while ((c = fgetc(stdin)) != EOF) {
if (off >= MAXSTR - 1) {
printf("Input too long.\n");
return 1;
}
in[off++] = (char)c;
}
in[off] = 0;
}
char out[10240];
int rc = miniver_translate(in, out, sizeof(out));
if (!rc) {
printf("%s", out);
}
return rc;
}
#endif
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
ABC_NAMESPACE_IMPL_END

View File

@ -1278,6 +1278,16 @@ static inline Vec_Int_t * Vec_IntInvert( Vec_Int_t * p, int Fill )
Vec_IntWriteEntry( vRes, Entry, i );
return vRes;
}
static inline Vec_Int_t * Vec_IntInvertSize( Vec_Int_t * p, int Size, int Fill )
{
Vec_Int_t * vMap = Vec_IntAlloc( 0 );
Vec_IntFill( vMap, Size, Fill );
int i, k;
Vec_IntForEachEntry( p, i, k )
if ( i != Fill )
Vec_IntWriteEntry( vMap, i, k );
return vMap;
}
/**Function*************************************************************