mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-14 19:25:40 +00:00
Merge pull request #209 from Silimate/opt_ffa_pt2
opt_first_fit_alloc updates + tests + some nice refactoring
This commit is contained in:
commit
6e80c5348d
9 changed files with 1758 additions and 148 deletions
|
|
@ -17,21 +17,25 @@
|
|||
*/
|
||||
|
||||
// Shared cut-region matching infrastructure for the functional rewrite
|
||||
// passes (opt_argmax, opt_priority_onehot, opt_compact_prefix). These passes
|
||||
// find combinational regions between "cut" signals (module ports, FF data
|
||||
// pins, or internal buses), verify their function by ConstEval
|
||||
// fingerprinting, and replace the region while leaving surrounding logic
|
||||
// untouched.
|
||||
// passes (opt_argmax, opt_priority_onehot, opt_compact_prefix,
|
||||
// opt_first_fit_alloc). These passes find combinational regions between
|
||||
// "cut" signals (module ports, FF data pins, or internal buses), verify
|
||||
// their function by ConstEval fingerprinting, and replace the region while
|
||||
// leaving surrounding logic untouched.
|
||||
//
|
||||
// This header is designed to be included INSIDE each pass's private
|
||||
// namespace (after PRIVATE_NAMESPACE_BEGIN), so the shared code has a single
|
||||
// source without introducing link-level coupling between the passes.
|
||||
// Tiny shared helpers (is_sequential, clog2_int, const_u64, ...) live in
|
||||
// rewrite_utils.h so non-cut-region passes can share them too.
|
||||
//
|
||||
// All graph walks and fingerprint evaluations are charged against
|
||||
// per-module work budgets so that adversarial netlist shapes (deep shared
|
||||
// cones with hundreds of same-width candidate buses) degrade into skipped
|
||||
// candidates instead of multi-minute runtimes.
|
||||
|
||||
#include "passes/opt/rewrite_utils.h"
|
||||
|
||||
struct CutRegionWorker
|
||||
{
|
||||
struct RootCand {
|
||||
|
|
@ -93,25 +97,6 @@ struct CutRegionWorker
|
|||
build_indexes();
|
||||
}
|
||||
|
||||
bool is_sequential(Cell *c)
|
||||
{
|
||||
return c->type.in(
|
||||
ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre),
|
||||
ID($_DFF_P_), ID($_DFF_N_),
|
||||
ID($_DFFE_PP_), ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_),
|
||||
ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
|
||||
ID($_DFF_NP0_), ID($_DFF_NP1_), ID($_DFF_NN0_), ID($_DFF_NN1_),
|
||||
ID($dlatch), ID($adlatch), ID($dlatchsr),
|
||||
ID($mem), ID($mem_v2), ID($meminit), ID($meminit_v2),
|
||||
ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2),
|
||||
ID($fsm),
|
||||
ID($assert), ID($assume), ID($cover), ID($live), ID($fair),
|
||||
ID($print), ID($check),
|
||||
ID($anyconst), ID($anyseq), ID($allconst), ID($allseq),
|
||||
ID($initstate));
|
||||
}
|
||||
|
||||
void build_indexes()
|
||||
{
|
||||
for (auto c : module->cells()) {
|
||||
|
|
@ -717,11 +702,7 @@ struct CutRegionWorker
|
|||
}
|
||||
|
||||
for (auto c : module->cells()) {
|
||||
if (!c->type.in(ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre),
|
||||
ID($dlatch), ID($adlatch), ID($dlatchsr)))
|
||||
continue;
|
||||
if (!c->hasPort(ID::D))
|
||||
if (!is_storage_ff(c) || !c->hasPort(ID::D))
|
||||
continue;
|
||||
consider_root(sigmap(c->getPort(ID::D)), stringf("%s.D", log_id(c->name)), true);
|
||||
}
|
||||
|
|
@ -796,26 +777,6 @@ struct CutRegionWorker
|
|||
return roots;
|
||||
}
|
||||
|
||||
// --- Small numeric helpers shared by the fingerprints. ---
|
||||
|
||||
static uint64_t lowmask_u64(int w)
|
||||
{
|
||||
if (w <= 0)
|
||||
return 0;
|
||||
if (w >= 64)
|
||||
return ~0ULL;
|
||||
return (1ULL << w) - 1;
|
||||
}
|
||||
|
||||
static Const const_u64(uint64_t value, int width)
|
||||
{
|
||||
vector<State> bits(width, State::S0);
|
||||
for (int i = 0; i < width && i < 64; i++)
|
||||
if ((value >> i) & 1ULL)
|
||||
bits[i] = State::S1;
|
||||
return Const(bits);
|
||||
}
|
||||
|
||||
SigSpec zext_sig(SigSpec sig, int width)
|
||||
{
|
||||
sig = sigmap(sig);
|
||||
|
|
|
|||
|
|
@ -28,19 +28,6 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static int clog2_int(int x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((1 << r) < x)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool is_power_of_two(int x)
|
||||
{
|
||||
return x > 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
static Const packed_table_const(const vector<uint64_t> &values, int elem_width)
|
||||
{
|
||||
vector<State> bits(values.size() * elem_width, State::S0);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -25,6 +25,8 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "passes/opt/rewrite_utils.h"
|
||||
|
||||
// Priority-encoder variants the pass recognises.
|
||||
enum class PEVariant { NONE, CLZ_FULL, CLZ_SHORT, CTZ_FULL, CTZ_SHORT };
|
||||
|
||||
|
|
@ -38,21 +40,6 @@ static const char* variant_name(PEVariant v) {
|
|||
}
|
||||
}
|
||||
|
||||
static int clog2_int(int x) {
|
||||
int r = 0;
|
||||
while ((1 << r) < x) r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Build an N-bit Const from a uint64_t pattern. Bit i set in `pattern` -> bit i
|
||||
// of the result. Bits beyond 64 are zero.
|
||||
static Const u64_const(uint64_t pattern, int N) {
|
||||
std::vector<State> bits(N, State::S0);
|
||||
for (int i = 0; i < N && i < 64; i++)
|
||||
if ((pattern >> i) & 1ULL) bits[i] = State::S1;
|
||||
return Const(bits);
|
||||
}
|
||||
|
||||
// Return the index of the highest set bit (MSB) of `c`, or -1 if all zero.
|
||||
static int const_msb_set(const Const& c, int N) {
|
||||
auto bits = c.to_bits();
|
||||
|
|
@ -99,24 +86,6 @@ struct OptPriEncWorker {
|
|||
|
||||
OptPriEncWorker(Module* m) : module(m), sigmap(m) { build_indexes(); }
|
||||
|
||||
bool is_sequential(Cell* c) {
|
||||
return c->type.in(
|
||||
ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre),
|
||||
ID($_DFF_P_), ID($_DFF_N_),
|
||||
ID($_DFFE_PP_), ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_),
|
||||
ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
|
||||
ID($_DFF_NP0_), ID($_DFF_NP1_), ID($_DFF_NN0_), ID($_DFF_NN1_),
|
||||
ID($dlatch), ID($adlatch), ID($dlatchsr),
|
||||
ID($mem), ID($mem_v2), ID($meminit), ID($meminit_v2),
|
||||
ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2),
|
||||
ID($fsm),
|
||||
ID($assert), ID($assume), ID($cover), ID($live), ID($fair),
|
||||
ID($print), ID($check),
|
||||
ID($anyconst), ID($anyseq), ID($allconst), ID($allseq),
|
||||
ID($initstate));
|
||||
}
|
||||
|
||||
void build_indexes() {
|
||||
for (auto cell : module->cells()) {
|
||||
if (is_sequential(cell)) {
|
||||
|
|
@ -213,7 +182,7 @@ struct OptPriEncWorker {
|
|||
// Build the test-vector deck for an N-bit input.
|
||||
vector<Const> gen_test_vectors(int N) {
|
||||
vector<Const> vs;
|
||||
vs.push_back(u64_const(0, N));
|
||||
vs.push_back(const_u64(0, N));
|
||||
for (int k = 0; k < N; k++) {
|
||||
std::vector<State> bits(N, State::S0);
|
||||
bits[k] = State::S1;
|
||||
|
|
|
|||
|
|
@ -264,26 +264,21 @@ struct OptPrioKeyWorker {
|
|||
range = 1ULL << cap;
|
||||
}
|
||||
ConstEval ce(module);
|
||||
uint64_t lfsr = 0x9e3779b97f4a7c15ULL ^ (uintptr_t)read;
|
||||
// Deterministic seed (not a Cell*): ASLR made uintptr_t seeding flake
|
||||
// across runs, so -strict could miss the OOR counterexample.
|
||||
uint64_t lfsr = 0x9e3779b97f4a7c15ULL ^ ((uint64_t)S << 1) ^
|
||||
((uint64_t)kw << 17) ^ (uint64_t)GetSize(steps);
|
||||
auto rnd = [&]() {
|
||||
lfsr ^= lfsr << 13; lfsr ^= lfsr >> 7; lfsr ^= lfsr << 17;
|
||||
return lfsr;
|
||||
};
|
||||
for (int t = 0; t < fp_trials; t++) {
|
||||
auto trial = [&](int rk, const vector<int> &kv, const vector<int> &gv) -> bool {
|
||||
ce.push();
|
||||
int rk = (int)(rnd() % range);
|
||||
ce.set(read_key, Const(rk, GetSize(read_key)));
|
||||
vector<int> kv(GetSize(steps));
|
||||
vector<int> gv(GetSize(steps));
|
||||
for (int i = 0; i < GetSize(steps); i++) {
|
||||
kv[i] = (int)(rnd() % range);
|
||||
ce.set(steps[i].key, Const(kv[i], GetSize(steps[i].key)));
|
||||
if (steps[i].guard == State::S1) {
|
||||
gv[i] = 1;
|
||||
} else {
|
||||
gv[i] = (int)(rnd() & 1);
|
||||
if (steps[i].guard != State::S1)
|
||||
ce.set(SigSpec(steps[i].guard), Const(gv[i], 1));
|
||||
}
|
||||
}
|
||||
SigSpec out(read->getPort(ID::Y));
|
||||
SigSpec undef;
|
||||
|
|
@ -293,7 +288,39 @@ struct OptPrioKeyWorker {
|
|||
for (int i = 0; i < GetSize(steps); i++)
|
||||
if (gv[i] && kv[i] == rk) { expect = 1; break; }
|
||||
ce.pop();
|
||||
if (!ok || actual != expect)
|
||||
return ok && actual == expect;
|
||||
};
|
||||
// Strict + non-pow2 S: force OOR key collisions the rewrite would accept
|
||||
// but the S-bit accumulator cannot store (taken[key>=S] is not a set bit).
|
||||
if (strict && range > (uint64_t)S && !steps.empty()) {
|
||||
int oor_n = (int)(range - (uint64_t)S);
|
||||
int forced = oor_n < 16 ? oor_n : 16;
|
||||
for (int f = 0; f < forced; f++) {
|
||||
int rk = S + f;
|
||||
vector<int> kv(GetSize(steps), 0);
|
||||
vector<int> gv(GetSize(steps), 0);
|
||||
kv[0] = rk;
|
||||
gv[0] = 1;
|
||||
for (int i = 1; i < GetSize(steps); i++) {
|
||||
kv[i] = (int)(rnd() % range);
|
||||
gv[i] = steps[i].guard == State::S1 ? 1 : (int)(rnd() & 1);
|
||||
}
|
||||
if (!trial(rk, kv, gv))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int t = 0; t < fp_trials; t++) {
|
||||
int rk = (int)(rnd() % range);
|
||||
vector<int> kv(GetSize(steps));
|
||||
vector<int> gv(GetSize(steps));
|
||||
for (int i = 0; i < GetSize(steps); i++) {
|
||||
kv[i] = (int)(rnd() % range);
|
||||
if (steps[i].guard == State::S1)
|
||||
gv[i] = 1;
|
||||
else
|
||||
gv[i] = (int)(rnd() & 1);
|
||||
}
|
||||
if (!trial(rk, kv, gv))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -29,19 +29,6 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static int clog2_int(int x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((1 << r) < x)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool is_power_of_two(int x)
|
||||
{
|
||||
return x > 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
// Pack a per-lane integer vector into a Const with elem_w bits per lane.
|
||||
static Const pack_lanes(const vector<int> &vals, int elem_w)
|
||||
{
|
||||
|
|
|
|||
105
passes/opt/rewrite_utils.h
Normal file
105
passes/opt/rewrite_utils.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2026 Silimate Inc. <akash@silimate.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
// Small shared helpers for Silimate functional-rewrite passes. Include inside
|
||||
// each pass's PRIVATE_NAMESPACE_BEGIN (same pattern as cut_region.h). Keep
|
||||
// cone/cut infrastructure in cut_region.h; put only type lists and tiny
|
||||
// numeric/Const helpers here so passes that do not use CutRegionWorker
|
||||
// (e.g. opt_prienc) can share them without pulling that machinery in.
|
||||
//
|
||||
// Guarded so a pass may include this directly and also via cut_region.h.
|
||||
|
||||
#ifndef SILIMATE_REWRITE_UTILS_H
|
||||
#define SILIMATE_REWRITE_UTILS_H
|
||||
|
||||
// Process-level / gate-level cells that terminate combinational cone walks.
|
||||
static inline bool is_sequential(Cell *c)
|
||||
{
|
||||
// Include $aldff/$aldffe: proc emits async-load FFs for async-reset
|
||||
// flops; opt later rewrites them to $adff. Omitting them lets get_cone
|
||||
// walk through the launch flop into D/CLK/ALOAD (e.g. full wdata).
|
||||
return c->type.in(
|
||||
ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($aldff), ID($aldffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre),
|
||||
ID($_DFF_P_), ID($_DFF_N_),
|
||||
ID($_DFFE_PP_), ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_),
|
||||
ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
|
||||
ID($_DFF_NP0_), ID($_DFF_NP1_), ID($_DFF_NN0_), ID($_DFF_NN1_),
|
||||
ID($dlatch), ID($adlatch), ID($dlatchsr),
|
||||
ID($mem), ID($mem_v2), ID($meminit), ID($meminit_v2),
|
||||
ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2),
|
||||
ID($fsm),
|
||||
ID($assert), ID($assume), ID($cover), ID($live), ID($fair),
|
||||
ID($print), ID($check),
|
||||
ID($anyconst), ID($anyseq), ID($allconst), ID($allseq),
|
||||
ID($initstate));
|
||||
}
|
||||
|
||||
// Clocked process-level FFs (no $ff, latches, or gate $_DFF_*). Used for
|
||||
// flop-D gather roots where Q/D semantics matter.
|
||||
static inline bool is_clocked_ff(Cell *c)
|
||||
{
|
||||
return c->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe),
|
||||
ID($aldff), ID($aldffe),
|
||||
ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), ID($dffsre));
|
||||
}
|
||||
|
||||
// Storage cells whose D port is a useful rewrite root (clocked FFs + $ff +
|
||||
// latches). Broader than is_clocked_ff; narrower than is_sequential.
|
||||
static inline bool is_storage_ff(Cell *c)
|
||||
{
|
||||
return is_clocked_ff(c) ||
|
||||
c->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr));
|
||||
}
|
||||
|
||||
// Ceiling of log2(x) for x >= 1; clog2_int(1) == 0. Same as Yosys $clog2 for
|
||||
// positive widths used by these passes.
|
||||
static inline int clog2_int(int x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((1 << r) < x)
|
||||
r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline bool is_power_of_two(int x)
|
||||
{
|
||||
return x > 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline uint64_t lowmask_u64(int w)
|
||||
{
|
||||
if (w <= 0)
|
||||
return 0;
|
||||
if (w >= 64)
|
||||
return ~0ULL;
|
||||
return (1ULL << w) - 1;
|
||||
}
|
||||
|
||||
// Pack the low `width` bits of `value` into a Const (bit i <- value bit i).
|
||||
static inline Const const_u64(uint64_t value, int width)
|
||||
{
|
||||
vector<State> bits(width, State::S0);
|
||||
for (int i = 0; i < width && i < 64; i++)
|
||||
if ((value >> i) & 1ULL)
|
||||
bits[i] = State::S1;
|
||||
return Const(bits);
|
||||
}
|
||||
|
||||
#endif // SILIMATE_REWRITE_UTILS_H
|
||||
|
|
@ -26,17 +26,6 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static int ceil_log2_int(int v)
|
||||
{
|
||||
int r = 0;
|
||||
int n = 1;
|
||||
while (n < v) {
|
||||
n <<= 1;
|
||||
r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#include "passes/opt/cut_region.h"
|
||||
|
||||
struct OptCompactPrefixWorker : CutRegionWorker
|
||||
|
|
@ -788,7 +777,7 @@ struct OptCompactPrefixWorker : CutRegionWorker
|
|||
{
|
||||
ref_cell = rw.anchor;
|
||||
int width = GetSize(rw.root);
|
||||
int count_width = ceil_log2_int(width + 1);
|
||||
int count_width = clog2_int(width + 1);
|
||||
SigSpec in_s = sigmap(rw.a);
|
||||
|
||||
vector<SigSpec> bits;
|
||||
|
|
@ -815,7 +804,7 @@ struct OptCompactPrefixWorker : CutRegionWorker
|
|||
int width = GetSize(rw.root);
|
||||
int loop_width = rw.loop_width;
|
||||
bool en_high = rw.msb_first; // polarity flag (see matching loop)
|
||||
int count_width = ceil_log2_int(loop_width + 1);
|
||||
int count_width = clog2_int(loop_width + 1);
|
||||
SigSpec dis_s = sigmap(rw.a);
|
||||
SigSpec data_s = sigmap(rw.b);
|
||||
|
||||
|
|
@ -863,7 +852,7 @@ struct OptCompactPrefixWorker : CutRegionWorker
|
|||
SigSpec n_s = sigmap(rw.b);
|
||||
bool msb_first = rw.msb_first;
|
||||
|
||||
int cnt_width = ceil_log2_int(width + 1);
|
||||
int cnt_width = clog2_int(width + 1);
|
||||
int table_size = 1 << cnt_width;
|
||||
int cmp_width = std::max(GetSize(n_s), cnt_width);
|
||||
|
||||
|
|
@ -933,7 +922,7 @@ struct OptCompactPrefixWorker : CutRegionWorker
|
|||
{
|
||||
ref_cell = rw.anchor;
|
||||
int width = GetSize(rw.root);
|
||||
int cnt_width = ceil_log2_int(width + 1);
|
||||
int cnt_width = clog2_int(width + 1);
|
||||
SigSpec en_s = sigmap(rw.a);
|
||||
SigSpec data_s = sigmap(rw.b);
|
||||
|
||||
|
|
|
|||
|
|
@ -857,3 +857,356 @@ design -load postopt
|
|||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# ============================================================================
|
||||
# Group I: exclusive saturating first-fit (no category / broadcast)
|
||||
# ============================================================================
|
||||
#
|
||||
# Plain taken[]/done[] scan without same-cat coalescing: each enabled lane
|
||||
# takes the next free slot until NB slots are exhausted. Later requesters get
|
||||
# rank 0 / not done. This is the qor_vmw_slot_lane shape.
|
||||
|
||||
# I1: exclusive dsel-only, N=8 NB=4 (equiv + fires).
|
||||
log -header "I1: exclusive saturating allocator N=8 NB=4 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=4, W=2) (
|
||||
input logic mode,
|
||||
input logic [N-1:0] req,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [N-1:0] en = req & {N{mode}};
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I2: exclusive with raw-input enable + sibling done bus, N=16 NB=4.
|
||||
log -header "I2: exclusive N=16 NB=4 raw-input en + done (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=16, NB=4, W=2) (
|
||||
input logic [N-1:0] lane_en,
|
||||
output logic [N*W-1:0] dsel_flat,
|
||||
output logic [N-1:0] done_o
|
||||
);
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (lane_en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
assign done_o = done;
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I3: exclusive with non-power-of-two slot budget (NB=3, W=2) -- learn nb=3.
|
||||
log -header "I3: exclusive NB=3 (non-power-of-two) N=8 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=3, W=2) (
|
||||
input logic [N-1:0] lane_en,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (lane_en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I4: exclusive last-fit near-miss (scan slots NB-1..0) -> no rewrite.
|
||||
log -header "I4: exclusive last-fit near-miss -> no rewrite"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=4, W=2) (
|
||||
input logic [N-1:0] lane_en,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (lane_en[i])
|
||||
for (int j=NB-1;j>=0;j--)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
opt_first_fit_alloc
|
||||
select -assert-count 0 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I5: exclusive MSB-first scan (lanes N-1..0), N=8 NB=4 (equiv).
|
||||
log -header "I5: exclusive MSB-first scan N=8 NB=4 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=4, W=2) (
|
||||
input logic [N-1:0] lane_en,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=N-1;i>=0;i--)
|
||||
if (lane_en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I6: exclusive + identity gather of wide lane payloads into NB slots
|
||||
# (qor_vmw_slot_lane shape: slot_data[j] = data[leader at j]).
|
||||
# DW != N so per-lane data buses are not mistaken for width-N enables.
|
||||
log -header "I6: exclusive + identity gather N=8 NB=4 DW=16 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=4, W=2, DW=16) (
|
||||
input logic [N-1:0] lane_en,
|
||||
input logic [N*DW-1:0] data_flat,
|
||||
output logic [N*W-1:0] dsel_flat,
|
||||
output logic [NB*DW-1:0] slot_flat
|
||||
);
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [DW-1:0] data [0:N-1];
|
||||
logic [DW-1:0] slot_data [0:NB-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
for (genvar g=0;g<N;g++) assign data[g] = data_flat[g*DW +: DW];
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
for (int j=0;j<NB;j++) slot_data[j] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (lane_en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
slot_data[j] = data[i];
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
for (genvar g=0;g<NB;g++) assign slot_flat[g*DW +: DW] = slot_data[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I7: exclusive and2 enable (req = a & b), with sibling done — the
|
||||
# qor_vmw_slot_lane enable shape after opt folds the named `req` wire.
|
||||
# Equiv covers the and2 fingerprint path; I6 already covers identity gather.
|
||||
log -header "I7: exclusive and2 enable + done N=16 NB=4 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=16, NB=4, W=2) (
|
||||
input logic [N-1:0] a,
|
||||
input logic [N-1:0] b,
|
||||
output logic [N*W-1:0] dsel_flat,
|
||||
output logic [N-1:0] done_o
|
||||
);
|
||||
logic [N-1:0] en = a & b;
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
assign done_o = done;
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I8: exclusive and2 enable without a global `opt` — launch flop stays a
|
||||
# plain $dff (sync reset) so leaf runs are data_q halves. Covers the pre-opt
|
||||
# and2 path; $aldff sequential handling is exercised by qor_vmw_slot_lane.
|
||||
log -header "I8: exclusive and2 enable pre-opt N=8 NB=4 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=8, NB=4, W=2) (
|
||||
input logic clk,
|
||||
input logic [2*N-1:0] wdata,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [2*N-1:0] data_q;
|
||||
always_ff @(posedge clk)
|
||||
data_q <= wdata;
|
||||
wire [N-1:0] req = data_q[N-1:0] & data_q[2*N-1:N];
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0;
|
||||
done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (req[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
# Intentionally no `opt`.
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
||||
# I9: exclusive with NB > max_therm_nb (8) — forces the binary saturating
|
||||
# Hillis–Steele fallback (emit_scan_exclusive_bin / emit_sat_add) instead of
|
||||
# the thermometer scan used by I1–I8.
|
||||
log -header "I9: exclusive binary-scan fallback N=16 NB=12 (equiv)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog -sv <<EOF
|
||||
module top #(parameter N=16, NB=12, W=4) (
|
||||
input logic mode,
|
||||
input logic [N-1:0] req,
|
||||
output logic [N*W-1:0] dsel_flat
|
||||
);
|
||||
logic [N-1:0] en = req & {N{mode}};
|
||||
logic [W-1:0] dsel [0:N-1];
|
||||
logic [NB-1:0] taken;
|
||||
logic [N-1:0] done;
|
||||
always_comb begin
|
||||
for (int i=0;i<N;i++) dsel[i] = '0;
|
||||
taken = '0; done = '0;
|
||||
for (int i=0;i<N;i++)
|
||||
if (en[i])
|
||||
for (int j=0;j<NB;j++)
|
||||
if (!taken[j] && !done[i]) begin
|
||||
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
|
||||
end
|
||||
end
|
||||
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
|
||||
endmodule
|
||||
EOF
|
||||
hierarchy -top top
|
||||
proc
|
||||
opt
|
||||
check -assert
|
||||
equiv_opt -assert opt_first_fit_alloc
|
||||
design -load postopt
|
||||
select -assert-min 1 w:*ffa_*
|
||||
design -reset
|
||||
log -pop
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue