mirror of
https://github.com/YosysHQ/yosys
synced 2026-06-20 15:50:27 +00:00
[core] add rf techlibs
This commit is contained in:
parent
54f8505045
commit
d7cf53d86a
49 changed files with 34443 additions and 0 deletions
553
techlibs/rapidflex/src/clock_buffer_cmd.cc
Normal file
553
techlibs/rapidflex/src/clock_buffer_cmd.cc
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "backends/rtlil/rtlil_backend.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "pugixml.hpp"
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct InsertClockBuffer : public Pass {
|
||||
InsertClockBuffer()
|
||||
: Pass("insert_clock_buffer",
|
||||
"This command is to insert clock buffer into the design") {}
|
||||
|
||||
/*utility function used by insert_ckbuff; copied from blif.cc*/
|
||||
const std::string str(RTLIL::IdString id) {
|
||||
std::string str = RTLIL::unescape_id(id);
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
|
||||
str[i] = '?';
|
||||
return str;
|
||||
}
|
||||
|
||||
/*utility function used by insert_ckbuff; copied from blif.cc*/
|
||||
const std::string str(RTLIL::SigBit sig) {
|
||||
if (sig.wire == NULL) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
std::string str = RTLIL::unescape_id(sig.wire->name);
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
|
||||
str[i] = '?';
|
||||
|
||||
if (sig.wire->width != 1)
|
||||
str +=
|
||||
stringf("[%d]", sig.wire->upto ? sig.wire->start_offset +
|
||||
sig.wire->width - sig.offset - 1
|
||||
: sig.wire->start_offset + sig.offset);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// eval_lut: Evaluate the output of a single LUT based on given input values
|
||||
// Parameters:
|
||||
// lut - Pointer to an RTLIL $lut cell
|
||||
// inputs - map<SigBit, bool> specifying the values of input signals
|
||||
// (can provide only a subset of inputs)
|
||||
// sigmap - SigMap, used to get the actual driving signal for each input
|
||||
// Returns:
|
||||
// Boolean output of the LUT (true/false)
|
||||
bool eval_lut(const RTLIL::Cell *lut, std::map<SigBit, bool> inputs,
|
||||
const SigMap &sigmap) {
|
||||
// Get the input vector of the LUT and map each signal to its actual driver
|
||||
SigSpec lut_inputs = sigmap(lut->getPort(ID::A));
|
||||
|
||||
// Number of LUT inputs
|
||||
int width = lut->getParam(ID::WIDTH).as_int();
|
||||
|
||||
// LUT truth table storing output for each input combination
|
||||
Const lut_table = lut->getParam(ID::LUT);
|
||||
|
||||
// Index into the LUT truth table
|
||||
int lut_index = 0;
|
||||
|
||||
// Iterate through each input bit
|
||||
for (int i = 0; i < width; i++) {
|
||||
// Get the i-th input signal and map it to its final driver
|
||||
SigBit bit = sigmap(lut_inputs[i]);
|
||||
|
||||
// Boolean value of the current input
|
||||
bool value;
|
||||
|
||||
// If the input value is provided by the user, use it
|
||||
if (inputs.count(bit))
|
||||
value = inputs[bit];
|
||||
// Otherwise, use the signal's default or constant value
|
||||
else
|
||||
value = SigSpec(bit).as_bool();
|
||||
|
||||
// Accumulate this bit into the LUT index
|
||||
// '<< i' places the value at the correct bit position
|
||||
lut_index |= (value << i);
|
||||
}
|
||||
|
||||
// Lookup the LUT output for the computed index and return as bool
|
||||
return lut_table.extract(lut_index).as_bool();
|
||||
}
|
||||
|
||||
/* This function rewires subckt such as flip-flops (FFs) and pcounter that use
|
||||
internally generated signals as their clock inputs. We can perform this
|
||||
rewiring before connecting the new clock buffer (ckbuf), because the new wires
|
||||
are added to the top module—not to the ckbuf itself. Therefore, once an
|
||||
internally generated clock is detected, a new wire is created in the top
|
||||
module and directly connected to the affected subckt. This rewiring process is
|
||||
independent of the addition of the ckbuf. */
|
||||
void rewire_subckt(RTLIL::Module *module, RTLIL::Cell *cell,
|
||||
RTLIL::IdString id_name, std::string C_input) {
|
||||
std::string C_output = C_input + "_ckbuf";
|
||||
if (!module->wire("\\" + C_output)) {
|
||||
auto output_wire = module->addWire("\\" + C_output, 1);
|
||||
}
|
||||
/* connect new ckbuf to the subckt */
|
||||
cell->unsetPort(id_name); // unsetPort("C")
|
||||
cell->setPort(id_name, module->wire("\\" + C_output));
|
||||
cell->fixup_parameters();
|
||||
}
|
||||
|
||||
/* This is a general-purpose function that works for all subcircuits (subckt).
|
||||
It detects clock and reset signals and determines whether they are global.
|
||||
If any global signal is detected, the function will rewire the subcircuit
|
||||
accordingly.
|
||||
*/
|
||||
void process_cell(RTLIL::Module *module, RTLIL::Cell *cell,
|
||||
std::map<int, RTLIL::Wire *> inputs,
|
||||
std::vector<RTLIL::IdString> clk_indicator_group,
|
||||
std::vector<RTLIL::IdString> reset_indicator_group,
|
||||
std::set<std::string> &ckbuf_info,
|
||||
std::map<std::string, std::string> &ckbuf_type) {
|
||||
RTLIL::IdString clk_indicator;
|
||||
for (auto clk : clk_indicator_group) {
|
||||
if (cell->hasPort(clk)) {
|
||||
clk_indicator = clk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// dff could have reset signal with keywords R or RN. We shall determine
|
||||
// which is the port name for current cell
|
||||
RTLIL::IdString reset_indicator;
|
||||
for (auto rst : reset_indicator_group) {
|
||||
if (cell->hasPort(rst)) {
|
||||
reset_indicator = rst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reset_indicator.empty() && clk_indicator.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool global_clock = false;
|
||||
bool global_reset = false;
|
||||
for (auto &it : inputs) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
if (cell->hasPort(clk_indicator)) {
|
||||
if (cell->getPort(clk_indicator) == RTLIL::SigSpec(wire, i)) {
|
||||
global_clock = true;
|
||||
continue; /*if the signal is global clock, then there is no need
|
||||
to check whether it is global reset or not*/
|
||||
}
|
||||
}
|
||||
|
||||
if (cell->hasPort(reset_indicator)) {
|
||||
if (cell->getPort(reset_indicator) == RTLIL::SigSpec(wire, i)) {
|
||||
global_reset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*grab the information of the internally generated clocks*/
|
||||
if (!global_clock && cell->hasPort(clk_indicator)) {
|
||||
std::string C_input = str(cell->getPort(clk_indicator)).c_str();
|
||||
ckbuf_info.insert(C_input);
|
||||
ckbuf_type[C_input] = "clock";
|
||||
rewire_subckt(module, cell, clk_indicator, C_input);
|
||||
}
|
||||
/*grab the information of the internally generated resets*/
|
||||
if (!global_reset && cell->hasPort(reset_indicator)) {
|
||||
std::string C_input = str(cell->getPort(reset_indicator)).c_str();
|
||||
ckbuf_info.insert(C_input);
|
||||
ckbuf_type[C_input] = "reset";
|
||||
rewire_subckt(module, cell, reset_indicator, C_input);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function examines sequential logic and returns a set of strings
|
||||
representing internally generated signals. When such a signal is found, it
|
||||
invokes rewire_subckt.*/
|
||||
std::set<std::string>
|
||||
find_internal_clk_r_signal(RTLIL::Module *module, RTLIL::Design *design,
|
||||
std::map<std::string, std::string> &ckbuf_type) {
|
||||
std::set<std::string> ckbuf_info;
|
||||
/*get input ports of the top module*/
|
||||
std::map<int, RTLIL::Wire *> inputs, outputs;
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->port_input)
|
||||
inputs[wire->port_id] = wire;
|
||||
}
|
||||
for (auto cell : module->cells()) {
|
||||
/*Bypass yosys internal cells $lut which doesn't have clock signal */
|
||||
if ((cell->type) == ID($lut)) {
|
||||
continue;
|
||||
} else {
|
||||
/*check whether the C port is internally generated clock signal or not*/
|
||||
std::vector<RTLIL::IdString> clk_indicator_group = {ID(C), ID(clk_i)};
|
||||
std::vector<RTLIL::IdString> reset_indicator_group = {ID(RN), ID(R),
|
||||
ID(rst_i)};
|
||||
process_cell(module, cell, inputs, clk_indicator_group,
|
||||
reset_indicator_group, ckbuf_info, ckbuf_type);
|
||||
}
|
||||
}
|
||||
return ckbuf_info;
|
||||
}
|
||||
|
||||
/* insert .subckt ckbuf for each internally generated clock or reset signal */
|
||||
void insert_ckbuf(RTLIL::Module *module,
|
||||
const std::set<std::string> &ckbuf_info) {
|
||||
for (const std::string &ckbuf : ckbuf_info) {
|
||||
RTLIL::Cell *ckbuf_cell =
|
||||
module->addCell(stringf("$ckbuf$%s", ckbuf.c_str()), "\\ckbuf");
|
||||
ckbuf_cell->setPort("\\in", module->wire("\\" + ckbuf));
|
||||
ckbuf_cell->setPort("\\out", module->wire("\\" + ckbuf + "_ckbuf"));
|
||||
ckbuf_cell->set_src_attribute("");
|
||||
}
|
||||
}
|
||||
|
||||
/*This function is for generating cell map file*/
|
||||
void generate_cell_map(const char *fname,
|
||||
const std::set<std::string> &ckbuf_info,
|
||||
const std::map<std::string, std::string> &ckbuf_type) {
|
||||
pugi::xml_document out_xml;
|
||||
pugi::xml_node root_node = out_xml.append_child("ckbuf_cell_map");
|
||||
for (const std::string &ckbuf : ckbuf_info) {
|
||||
std::string in = ckbuf;
|
||||
std::string out = ckbuf + "_ckbuf";
|
||||
std::string type;
|
||||
|
||||
auto it = ckbuf_type.find(ckbuf);
|
||||
if (it != ckbuf_type.end()) {
|
||||
type = it->second;
|
||||
} else {
|
||||
log_error("No port type defined for ckbuf %s \n", out.c_str());
|
||||
}
|
||||
|
||||
pugi::xml_node ckbuf_node = root_node.append_child("ckbuf");
|
||||
ckbuf_node.append_attribute("input_net") = in.c_str();
|
||||
ckbuf_node.append_attribute("cell") = out.c_str();
|
||||
ckbuf_node.append_attribute("type") = type.c_str();
|
||||
}
|
||||
out_xml.save_file(fname);
|
||||
log("cell map is stored in file %s \n", fname);
|
||||
}
|
||||
void rewire_lut_primitive(RTLIL::Cell *cell,
|
||||
const std::vector<RTLIL::SigBit> &sig,
|
||||
const RTLIL::Const &new_lut, int new_width) {
|
||||
cell->unsetPort(ID::A);
|
||||
cell->setPort(ID::A, sig);
|
||||
|
||||
// Update LUT parameters
|
||||
cell->parameters[ID::LUT] = new_lut; // Set new truth table
|
||||
cell->parameters[ID::WIDTH] = new_width;
|
||||
cell->fixup_parameters();
|
||||
}
|
||||
|
||||
void process_cell_rewire_lut(
|
||||
RTLIL::Module *module, RTLIL::Cell *cell,
|
||||
const std::map<std::string, std::set<RTLIL::SigBit>>
|
||||
&internal_signal_io_map,
|
||||
const std::map<std::string, std::string> &internal_signal_lut,
|
||||
const Yosys::SigMap &sigmap) {
|
||||
std::set<RTLIL::SigBit> sig;
|
||||
/* sig and ordered_sig contain the same signals.
|
||||
* sig is used to store the rewired signals and avoid duplicates,
|
||||
* while ordered_sig preserves the signal order so it stays aligned
|
||||
* with the truth table information.
|
||||
*/
|
||||
std::vector<RTLIL::SigBit> ordered_sig;
|
||||
std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> sig_replace_port_map;
|
||||
std::map<RTLIL::SigBit, RTLIL::IdString> sig_replace_cell_map;
|
||||
std::set<RTLIL::SigBit> temp_sig;
|
||||
std::set<RTLIL::SigBit> lut_inputs = cell->getPort(ID::A).to_sigbit_set();
|
||||
std::vector<RTLIL::SigBit> lut_inputs_vector =
|
||||
cell->getPort(ID::A).to_sigbit_vector();
|
||||
std::set<RTLIL::SigBit> common_port_all;
|
||||
std::vector<bool> replaced_boolean_value;
|
||||
std::vector<bool> remained_boolean_value;
|
||||
bool rewire_required = false;
|
||||
std::vector<RTLIL::State> old_lut =
|
||||
cell->parameters.at(ID::LUT).bits(); // Original LUT truth table
|
||||
for (const auto &[internal_signal_output, internal_signal_input] :
|
||||
internal_signal_io_map) {
|
||||
std::set<RTLIL::SigBit> common_port;
|
||||
std::string mapped_ckbuf_name;
|
||||
if (std::includes(lut_inputs.begin(), lut_inputs.end(),
|
||||
internal_signal_input.begin(),
|
||||
internal_signal_input.end())) {
|
||||
std::set_intersection(lut_inputs.begin(), lut_inputs.end(),
|
||||
internal_signal_input.begin(),
|
||||
internal_signal_input.end(),
|
||||
std::inserter(common_port, common_port.end()));
|
||||
std::string C_output = internal_signal_output + "_ckbuf";
|
||||
mapped_ckbuf_name = internal_signal_output;
|
||||
if (!module->wire("\\" + C_output)) {
|
||||
log("wire %s is not defined", C_output.c_str());
|
||||
}
|
||||
auto replaced_sig = module->wire("\\" + C_output);
|
||||
if (sig.find(replaced_sig) != sig.end()) {
|
||||
/* element has already been recorded*/
|
||||
log("signal %s has been replaced!\n", C_output.c_str());
|
||||
continue;
|
||||
}
|
||||
sig.insert(replaced_sig);
|
||||
if (std::find(ordered_sig.begin(), ordered_sig.end(), replaced_sig) ==
|
||||
ordered_sig.end()) {
|
||||
ordered_sig.push_back(replaced_sig);
|
||||
}
|
||||
sig_replace_port_map[replaced_sig] = common_port;
|
||||
auto src_cell = internal_signal_lut.at(mapped_ckbuf_name);
|
||||
sig_replace_cell_map[replaced_sig] = src_cell;
|
||||
rewire_required = true;
|
||||
common_port_all.insert(common_port.begin(), common_port.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (rewire_required) {
|
||||
// For example, the following lut will be rewired
|
||||
// .names a b internal_clock1
|
||||
// .names a b c out
|
||||
// as
|
||||
// .names internal_clock1_ckbuf c out
|
||||
// where internal_clock1 is the original internal signals and
|
||||
// internal_clock1_ckbuf is the outputs of ckbuf. (.subckt ckbuf
|
||||
// internal_clock1 internal_clock1_ckbuf)
|
||||
// we need to update the truth table of .names internal_clock1_ckbuf
|
||||
// c out concurrently
|
||||
/* The first step is to get the original truth table, i.e. the truth
|
||||
* table of .names a b c d out */
|
||||
int bit_width = lut_inputs_vector.size();
|
||||
std::vector<std::vector<bool>> original_truth_table;
|
||||
std::map<RTLIL::SigBit, int> sig_index_map;
|
||||
|
||||
for (size_t index = 0; index < lut_inputs_vector.size(); index++) {
|
||||
sig_index_map[lut_inputs_vector[index]] = index;
|
||||
}
|
||||
|
||||
for (int index = 0; index < old_lut.size(); index++) {
|
||||
if (old_lut[index] == RTLIL::State::S1) {
|
||||
std::vector<bool> bits(bit_width, false);
|
||||
for (int b = 0; b < bit_width; b++) {
|
||||
bits[b] = (index >> b) & 1;
|
||||
}
|
||||
original_truth_table.push_back(bits);
|
||||
}
|
||||
}
|
||||
|
||||
/* get remained sigs */
|
||||
std::set_difference(lut_inputs.begin(), lut_inputs.end(),
|
||||
common_port_all.begin(), common_port_all.end(),
|
||||
std::inserter(temp_sig, temp_sig.end()));
|
||||
|
||||
std::vector<std::vector<bool>> modified_bit;
|
||||
|
||||
for (const auto &line : original_truth_table) {
|
||||
std::vector<bool> remained_bit;
|
||||
/* get sigs that can be replaced by ckbuf and evaluate its truth
|
||||
* table values and updates the final truth table*/
|
||||
for (auto replaced_sig : sig) {
|
||||
const RTLIL::Cell *cell_temp =
|
||||
module->cell(sig_replace_cell_map[replaced_sig]);
|
||||
auto ports = sig_replace_port_map[replaced_sig];
|
||||
|
||||
std::map<RTLIL::SigBit, bool> common_port_map;
|
||||
for (auto port : ports) {
|
||||
common_port_map[port] = line[sig_index_map[port]];
|
||||
}
|
||||
|
||||
bool changed_bit = eval_lut(cell_temp, common_port_map, sigmap);
|
||||
remained_bit.push_back(changed_bit);
|
||||
}
|
||||
/* get remained sigs and use previous truth table values*/
|
||||
for (auto port : temp_sig) {
|
||||
remained_bit.push_back(line[sig_index_map[port]]);
|
||||
}
|
||||
|
||||
modified_bit.push_back(remained_bit);
|
||||
}
|
||||
/* get the final truth table of the rewired lut such as .names
|
||||
* internal_clock1_ckbuf c out */
|
||||
std::vector<int> modified_bit_int;
|
||||
for (const auto &line : modified_bit) {
|
||||
int value = 0;
|
||||
for (size_t i = 0; i < line.size(); i++) {
|
||||
if (line[i]) {
|
||||
value |= (1 << i);
|
||||
}
|
||||
}
|
||||
modified_bit_int.push_back(value);
|
||||
}
|
||||
|
||||
sig.insert(temp_sig.begin(), temp_sig.end());
|
||||
ordered_sig.insert(ordered_sig.end(), temp_sig.begin(), temp_sig.end());
|
||||
|
||||
int new_width = sig.size();
|
||||
int num_entries = 1 << new_width;
|
||||
RTLIL::Const new_lut(num_entries);
|
||||
/*initialize lut value to 0*/
|
||||
for (int i = 0; i < num_entries; i++) {
|
||||
new_lut.bits()[i] = RTLIL::State::S0;
|
||||
}
|
||||
/* assign final truth table's info to current cell */
|
||||
for (int i : modified_bit_int) {
|
||||
new_lut.bits()[i] = RTLIL::State::S1;
|
||||
}
|
||||
|
||||
rewire_lut_primitive(cell, ordered_sig, new_lut, new_width);
|
||||
module->fixup_ports();
|
||||
std::set<RTLIL::SigBit> lut_outputs =
|
||||
cell->getPort(ID::Y).to_sigbit_set();
|
||||
}
|
||||
}
|
||||
/* This function rewires luts which have internally generated signals as its
|
||||
* input*/
|
||||
void rewire_luts(RTLIL::Module *module,
|
||||
const std::set<std::string> &ckbuf_info) {
|
||||
/* find the lut that has internally generated clock as an output and get its
|
||||
* io map */
|
||||
Yosys::SigMap sigmap(module);
|
||||
std::map<std::string, std::set<RTLIL::SigBit>> internal_signal_io_map;
|
||||
std::map<std::string, std::string> internal_signal_lut;
|
||||
std::set<RTLIL::SigBit> flattened_io_map;
|
||||
/*the first for loop constructs the map between internall signal name and
|
||||
its corresponding fan-ins For example, consider the following netlist:
|
||||
.names a b internal_clock1
|
||||
.names c d internal_clock2
|
||||
The internal_signal_io_map stores key-value pairs from all luts like this:
|
||||
[internal_clock1, {a, b}], [internal_clock2, {c, d}].
|
||||
*/
|
||||
for (const auto cell : module->cells()) {
|
||||
if ((cell->type) == ID($lut)) {
|
||||
auto &inputs = cell->getPort(ID::A);
|
||||
std::string output = str(cell->getPort(ID::Y));
|
||||
auto width = cell->parameters.at(ID::WIDTH).as_int();
|
||||
log_assert(inputs.size() == width);
|
||||
if (ckbuf_info.find(output) != ckbuf_info.end()) {
|
||||
/* We assume that all LUT cells are explicitly named before the
|
||||
current pass. Anonymous cells indicate an unexpected state after
|
||||
techmapping and are treated as a fatal error. */
|
||||
if (cell->name.empty()) {
|
||||
log_error("cell->name is empty for output=%s\n", output.c_str());
|
||||
}
|
||||
auto io_set = inputs.to_sigbit_set();
|
||||
internal_signal_io_map[output] = io_set;
|
||||
internal_signal_lut[output] = cell->name.c_str();
|
||||
flattened_io_map.insert(internal_signal_io_map[output].begin(),
|
||||
internal_signal_io_map[output].end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This for loop does two things:
|
||||
1. detect logic that can be replaced by internal signals
|
||||
For example, consider the following netlist:
|
||||
.names a b internal_clock1
|
||||
.names c d internal_clock2
|
||||
The internal_signal_io_map stores key-value pairs from all luts like
|
||||
this: [internal_clock1, {a, b}], [internal_clock2, {c, d}]. When we
|
||||
encounter a netlist like: .names a b c d out we can replace it with .names
|
||||
internal_clock1 internal_clock2 out
|
||||
|
||||
2. rewire luts
|
||||
For example, the following lut will be rewired
|
||||
.names internal_clock1 internal_clock2 out
|
||||
as
|
||||
.names internal_clock1_ckbuf internal_clock2_ckbuf out_ckbuf
|
||||
where internal_clock1/2 are the original internal signals and
|
||||
internal_clock1_ckbuf/2_ckbuf are the outputs of ckbuf. (.subckt ckbuf
|
||||
internal_clock1 internal_clock1_ckbuf)
|
||||
*/
|
||||
for (auto cell : module->cells()) {
|
||||
if ((cell->type) == ID($lut)) {
|
||||
std::string output = str(cell->getPort(ID::Y));
|
||||
if (ckbuf_info.find(output) != ckbuf_info.end()) {
|
||||
continue; /*by pass the luts that generate internal clk/reset */
|
||||
} else {
|
||||
process_cell_rewire_lut(module, cell, internal_signal_io_map,
|
||||
internal_signal_lut, sigmap);
|
||||
/*replace internal clk/reset with clk/reset_buf signal */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
log("Arguments to the command insert_clock_buffer:\n");
|
||||
std::string top_module_name;
|
||||
std::string cell_map_file;
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
log(" %s\n", args[i].c_str());
|
||||
|
||||
if (args[i] == "-top" && i + 1 < args.size()) {
|
||||
top_module_name = args[i + 1];
|
||||
} else if (args[i] == "-cell_map_file" && i + 1 < args.size()) {
|
||||
cell_map_file = args[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (cell_map_file.empty()) {
|
||||
cell_map_file = "cell_map.xml";
|
||||
}
|
||||
log("cell map location is %s \n", cell_map_file.c_str());
|
||||
|
||||
/*if top_module_name is empty, get it from design*/
|
||||
if (top_module_name.empty())
|
||||
for (auto module : design->modules())
|
||||
if (module->get_bool_attribute(ID::top))
|
||||
top_module_name = module->name.str();
|
||||
|
||||
if (top_module_name.empty()) {
|
||||
log_error("No top module detected. Insert clock buffer failed.");
|
||||
} else {
|
||||
log("Top module in current design: %s \n", top_module_name.c_str());
|
||||
}
|
||||
|
||||
/*Insert clock buffer into the top module*/
|
||||
design->sort();
|
||||
for (auto module : design->modules()) {
|
||||
/*only insert buffer to top module*/
|
||||
if (module->name == RTLIL::escape_id(top_module_name)) {
|
||||
std::map<std::string, std::string> ckbuf_type;
|
||||
std::set<std::string> ckbuf_info =
|
||||
find_internal_clk_r_signal(module, design, ckbuf_type);
|
||||
/*insert ckbuf and rewire dff */
|
||||
insert_ckbuf(module, ckbuf_info);
|
||||
|
||||
module->fixup_ports();
|
||||
/*rewire luts */
|
||||
rewire_luts(module, ckbuf_info);
|
||||
|
||||
module->fixup_ports();
|
||||
/* print out cells */
|
||||
if (!ckbuf_info.empty()) {
|
||||
generate_cell_map(cell_map_file.c_str(), ckbuf_info, ckbuf_type);
|
||||
} else {
|
||||
log("Ckbuf info is empty. No cell map file will be generated! \n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
design->check();
|
||||
}
|
||||
} Insert_clock_buffer;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
12313
techlibs/rapidflex/src/pugixml.cpp
Normal file
12313
techlibs/rapidflex/src/pugixml.cpp
Normal file
File diff suppressed because it is too large
Load diff
1544
techlibs/rapidflex/src/pugixml.hpp
Normal file
1544
techlibs/rapidflex/src/pugixml.hpp
Normal file
File diff suppressed because it is too large
Load diff
192
techlibs/rapidflex/src/rf_dsp_mad.cc
Normal file
192
techlibs/rapidflex/src/rf_dsp_mad.cc
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "pmgen/rf_dsp_mad.h"
|
||||
|
||||
static void create_rf_mad_dsp(rf_dsp_mad_pm &pm) {
|
||||
auto &st = pm.st_rf_dsp_mad;
|
||||
|
||||
// Reject if multiplier drives anything else than $add
|
||||
if (st.mul_nusers > 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get port widths
|
||||
size_t a_width = GetSize(st.mul->getPort(ID(A)));
|
||||
size_t b_width = GetSize(st.mul->getPort(ID(B)));
|
||||
size_t c_width = GetSize(st.add->getPort(ID(A)));
|
||||
if (st.add_ba == ID(B)) {
|
||||
c_width = GetSize(st.add->getPort(ID(B)));
|
||||
}
|
||||
size_t z_width = GetSize(st.add->getPort(ID(Y)));
|
||||
|
||||
size_t min_width = std::min(a_width, b_width);
|
||||
size_t max_width = std::max(a_width, b_width);
|
||||
|
||||
// Signed / unsigned
|
||||
bool a_signed = st.mul->getParam(ID(A_SIGNED)).as_bool();
|
||||
bool b_signed = st.mul->getParam(ID(B_SIGNED)).as_bool();
|
||||
bool c_signed = st.add->getParam(ID(A_SIGNED)).as_bool();
|
||||
if (st.add_ba == ID(B)) {
|
||||
c_signed = st.add->getParam(ID(B_SIGNED)).as_bool();
|
||||
}
|
||||
|
||||
// Determine DSP type or discard if too narrow / wide
|
||||
RTLIL::IdString type;
|
||||
size_t tgt_a_width;
|
||||
size_t tgt_b_width;
|
||||
size_t tgt_c_width;
|
||||
size_t tgt_z_width;
|
||||
|
||||
string cell_base_name = "mad";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
|
||||
if (min_width <= 2 && max_width <= 2 && z_width <= 4) {
|
||||
// Too narrow
|
||||
return;
|
||||
} else if (min_width <= 12 && max_width <= 10 && z_width <= 22) {
|
||||
cell_size_name = "12x10x22";
|
||||
tgt_a_width = 12;
|
||||
tgt_b_width = 10;
|
||||
tgt_c_width = 22;
|
||||
tgt_z_width = 22;
|
||||
} else if (min_width <= 24 && max_width <= 20 && z_width <= 44) {
|
||||
cell_size_name = "24x20x44";
|
||||
tgt_a_width = 24;
|
||||
tgt_b_width = 20;
|
||||
tgt_c_width = 44;
|
||||
tgt_z_width = 44;
|
||||
} else {
|
||||
// Too wide
|
||||
return;
|
||||
}
|
||||
|
||||
cell_full_name = cell_base_name + cell_size_name + cell_cfg_name;
|
||||
|
||||
type = RTLIL::escape_id(cell_full_name);
|
||||
log("Inferring MAD %zux%zu+%zu->%zu as %s from:\n", a_width, b_width, c_width,
|
||||
z_width, RTLIL::unescape_id(type).c_str());
|
||||
|
||||
for (auto cell : {st.mul, st.add}) {
|
||||
if (cell != nullptr) {
|
||||
log(" %s (%s)\n", RTLIL::unescape_id(cell->name).c_str(),
|
||||
RTLIL::unescape_id(cell->type).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Build the DSP cell name
|
||||
std::string name;
|
||||
name += RTLIL::unescape_id(st.mul->name) + "_";
|
||||
name += RTLIL::unescape_id(st.add->name) + "_";
|
||||
|
||||
// Add the DSP cell
|
||||
RTLIL::Cell *cell = pm.module->addCell(RTLIL::escape_id(name), type);
|
||||
|
||||
// Set attributes
|
||||
cell->set_bool_attribute(RTLIL::escape_id("is_inferred"), true);
|
||||
|
||||
// Get input/output data signals
|
||||
RTLIL::SigSpec sig_a;
|
||||
RTLIL::SigSpec sig_b;
|
||||
RTLIL::SigSpec sig_c;
|
||||
RTLIL::SigSpec sig_z;
|
||||
|
||||
if (a_width >= b_width) {
|
||||
sig_a = st.mul->getPort(ID(A));
|
||||
sig_b = st.mul->getPort(ID(B));
|
||||
} else {
|
||||
sig_a = st.mul->getPort(ID(B));
|
||||
sig_b = st.mul->getPort(ID(A));
|
||||
}
|
||||
|
||||
sig_c = st.add->getPort(ID(A));
|
||||
if (st.add_ba == ID(B)) {
|
||||
sig_c = st.add->getPort(ID(B));
|
||||
}
|
||||
sig_z = st.add->getPort(ID(Y));
|
||||
|
||||
// Connect input data ports, sign extend / pad with zeros
|
||||
sig_a.extend_u0(tgt_a_width, a_signed);
|
||||
sig_b.extend_u0(tgt_b_width, b_signed);
|
||||
sig_c.extend_u0(tgt_c_width, c_signed);
|
||||
cell->setPort(RTLIL::escape_id("A0"), sig_a);
|
||||
cell->setPort(RTLIL::escape_id("B0"), sig_b);
|
||||
|
||||
// Connect input data port, pad if needed
|
||||
if ((size_t)GetSize(sig_c) < tgt_c_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_c_width - GetSize(sig_c));
|
||||
sig_c.append(wire);
|
||||
}
|
||||
cell->setPort(RTLIL::escape_id("C0"), sig_c);
|
||||
|
||||
// Connect output data port, pad if needed
|
||||
if ((size_t)GetSize(sig_z) < tgt_z_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
|
||||
sig_z.append(wire);
|
||||
}
|
||||
cell->setPort(RTLIL::escape_id("Y"), sig_z);
|
||||
|
||||
bool subtract = (st.add->type == RTLIL::escape_id("$sub"));
|
||||
if (subtract) {
|
||||
cell->setPort(RTLIL::escape_id("subtract_i"),
|
||||
RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0));
|
||||
}
|
||||
|
||||
// Mark the cells for removal
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.add);
|
||||
}
|
||||
|
||||
struct RfDspMacc : public Pass {
|
||||
// Local variables
|
||||
bool show_help;
|
||||
|
||||
RfDspMacc()
|
||||
: Pass("rf_dsp_mad", "Extract multiply-add and multiply-subtract "
|
||||
"operators and map to dedicated DSPs") {}
|
||||
|
||||
void help() override {
|
||||
log("\n");
|
||||
log(" rf_dsp_mad [options] [selection]\n");
|
||||
log("\n");
|
||||
log(" Extract multiply-add and multiply-subtract operators and map to "
|
||||
"dedicated DSPs\n");
|
||||
log("\n");
|
||||
log(" -help: show help desk\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void clear_flags() override { show_help = false; }
|
||||
|
||||
void execute(std::vector<std::string> a_Args,
|
||||
RTLIL::Design *a_Design) override {
|
||||
log_header(a_Design, "Executing RF_DSP_MAD pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
if (a_Args[argidx] == "-help") {
|
||||
show_help = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
if (show_help) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
rf_dsp_mad_pm(module, module->selected_cells())
|
||||
.run_rf_dsp_mad(create_rf_mad_dsp);
|
||||
}
|
||||
}
|
||||
|
||||
} RfDspMad;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
26
techlibs/rapidflex/src/rf_dsp_mad.pmg
Normal file
26
techlibs/rapidflex/src/rf_dsp_mad.pmg
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
pattern rf_dsp_mad
|
||||
|
||||
state <IdString> add_ba
|
||||
|
||||
state <int> mul_nusers
|
||||
state <int> add_nusers
|
||||
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
select nusers(port(mul, \Y)) <= 3
|
||||
set mul_nusers nusers(port(mul, \Y))
|
||||
endmatch
|
||||
|
||||
match add
|
||||
select add->type.in($add, $sub)
|
||||
choice <IdString> AB {\A, \B}
|
||||
define <IdString> BA (AB == \A ? \B : \A)
|
||||
index <SigSpec> port(add, AB) === port(mul, \Y)
|
||||
select nusers(port(add, \Y)) <= 3
|
||||
set add_nusers nusers(port(add, \Y))
|
||||
set add_ba BA
|
||||
endmatch
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
||||
441
techlibs/rapidflex/src/rf_new_dsp.cc
Normal file
441
techlibs/rapidflex/src/rf_new_dsp.cc
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "pmgen/rf_new_dsp.h"
|
||||
|
||||
void swapinput(RTLIL::SigSpec &sigA, RTLIL::SigSpec &sigB) {
|
||||
if (GetSize(sigA) < GetSize(sigB)) {
|
||||
RTLIL::SigSpec sigC = sigB;
|
||||
sigB = sigA;
|
||||
sigA = sigC;
|
||||
}
|
||||
}
|
||||
|
||||
void rf_new_dsp(rf_new_dsp_pm &pm) {
|
||||
auto &st = pm.st_rf_new_dsp;
|
||||
|
||||
log("mul1: %s\n", log_id(st.mul1, "--"));
|
||||
log("mul2: %s\n", log_id(st.mul2, "--"));
|
||||
log("mul3: %s\n", log_id(st.mul3, "--"));
|
||||
log("mul4: %s\n", log_id(st.mul4, "--"));
|
||||
log("postAdd1: %s\n", log_id(st.postAdd1, "--"));
|
||||
log("postAdd2: %s\n", log_id(st.postAdd2, "--"));
|
||||
log("postAdd3: %s\n", log_id(st.postAdd3, "--"));
|
||||
log("postAdd4: %s\n", log_id(st.postAdd4, "--"));
|
||||
|
||||
RTLIL::SigSpec sigA, sigB, sigD, sigY;
|
||||
|
||||
// mode
|
||||
string mode;
|
||||
if (st.level == 4) {
|
||||
if (st.dinput)
|
||||
mode +=
|
||||
"1001"; // 4-level mac with d input: d + a1*b1 + a2*b2 + a3*b3 + a4*b4
|
||||
else
|
||||
mode +=
|
||||
"0000"; // 4-level mac without d input: a1*b1 + a2*b2 + a3*b3 + a4*b4
|
||||
}
|
||||
if (st.level == 3)
|
||||
mode += "1001"; // 3-level mac with d input: d + a1*b1 + a2*b2 + a3*b3
|
||||
if (st.level == 2) {
|
||||
if (st.dinput)
|
||||
mode += "0001"; // 2-level mac with d input: d + a1*b1 + a2*b2
|
||||
else
|
||||
mode += "0010"; // 2-level mac without d input: a1*b1 + a2*b2
|
||||
}
|
||||
if (st.level == 1) {
|
||||
if (st.dinput)
|
||||
mode += "0101"; // 1-level mac with d input: d + a1*b1
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// input size
|
||||
int n_size = 0;
|
||||
int m_size = 0;
|
||||
int d_size = 0;
|
||||
|
||||
string cell_base_name = "mad";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
|
||||
if (st.mul1) {
|
||||
swapinput(st.sigA1, st.sigB1);
|
||||
n_size = n_size > GetSize(st.sigA1) ? n_size : GetSize(st.sigA1);
|
||||
m_size = m_size > GetSize(st.sigB1) ? m_size : GetSize(st.sigB1);
|
||||
}
|
||||
|
||||
if (st.mul2) {
|
||||
swapinput(st.sigA2, st.sigB2);
|
||||
n_size = n_size > GetSize(st.sigA2) ? n_size : GetSize(st.sigA2);
|
||||
m_size = m_size > GetSize(st.sigB2) ? m_size : GetSize(st.sigB2);
|
||||
}
|
||||
|
||||
if (st.mul3) {
|
||||
swapinput(st.sigA3, st.sigB3);
|
||||
n_size = n_size > GetSize(st.sigA3) ? n_size : GetSize(st.sigA3);
|
||||
m_size = m_size > GetSize(st.sigB3) ? m_size : GetSize(st.sigB3);
|
||||
}
|
||||
|
||||
if (st.mul4) {
|
||||
swapinput(st.sigA4, st.sigB4);
|
||||
n_size = n_size > GetSize(st.sigA4) ? n_size : GetSize(st.sigA4);
|
||||
m_size = m_size > GetSize(st.sigB4) ? m_size : GetSize(st.sigB4);
|
||||
}
|
||||
|
||||
if (st.dinput)
|
||||
d_size = GetSize(st.sigD);
|
||||
|
||||
if (mode == "0100") {
|
||||
n_size = (n_size + 1) / 2;
|
||||
m_size = (m_size + 1) / 2;
|
||||
}
|
||||
|
||||
if (n_size <= 2 && m_size <= 2 && d_size <= 4) {
|
||||
// Too narrow
|
||||
return;
|
||||
} else if (n_size <= 12 && m_size <= 10 && d_size <= 30) {
|
||||
cell_size_name = "12x10x22";
|
||||
n_size = 12;
|
||||
m_size = 10;
|
||||
d_size = 30;
|
||||
} else if (n_size <= 24 && m_size <= 20 && d_size <= 52) {
|
||||
cell_size_name = "24x20x44";
|
||||
n_size = 24;
|
||||
m_size = 20;
|
||||
d_size = 52;
|
||||
} else {
|
||||
// Too wide
|
||||
return;
|
||||
}
|
||||
|
||||
// cell
|
||||
cell_full_name = cell_base_name + cell_size_name + cell_cfg_name;
|
||||
|
||||
string cellname;
|
||||
cellname += "newdsp_" + RTLIL::unescape_id(st.mul1->name);
|
||||
RTLIL::Cell *cell = pm.module->addCell(RTLIL::escape_id(cellname),
|
||||
RTLIL::escape_id(cell_full_name));
|
||||
|
||||
// D input
|
||||
bool d_signed = false;
|
||||
if (st.dinput) {
|
||||
d_signed = st.postAdd1->getParam(ID(A_SIGNED)).as_bool();
|
||||
if (mode == "0001")
|
||||
sigD.extend_u0(d_size);
|
||||
sigD.append(st.sigD);
|
||||
}
|
||||
sigD.extend_u0(2 * d_size, d_signed);
|
||||
|
||||
// output
|
||||
if (st.multiout2 || st.multiout3) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, d_size - GetSize(st.sigY2));
|
||||
sigY.append(st.sigY2);
|
||||
sigY.append(wire);
|
||||
sigY.append(st.sigY);
|
||||
} else if (mode == "0001" || (st.level == 4 && st.dinput)) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, d_size);
|
||||
sigY.append(wire);
|
||||
sigY.append(st.sigY);
|
||||
} else
|
||||
sigY.append(st.sigY);
|
||||
auto *wire = pm.module->addWire(NEW_ID, 2 * d_size - GetSize(sigY));
|
||||
sigY.append(wire);
|
||||
|
||||
// input
|
||||
bool a_signed, b_signed;
|
||||
if (mode == "0001") {
|
||||
sigA.extend_u0(2 * n_size);
|
||||
sigB.extend_u0(2 * m_size);
|
||||
}
|
||||
if (st.mul1 && mode != "0100") {
|
||||
a_signed = st.mul1->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul1->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA1.extend_u0(n_size, a_signed);
|
||||
st.sigB1.extend_u0(m_size, b_signed);
|
||||
sigA.append(st.sigA1);
|
||||
sigB.append(st.sigB1);
|
||||
}
|
||||
if (mode == "0100") {
|
||||
a_signed = st.mul1->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul1->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA1.extend_u0(2 * n_size, a_signed);
|
||||
st.sigB1.extend_u0(2 * m_size, b_signed);
|
||||
sigA.append(st.sigA1);
|
||||
sigB.append(st.sigB1);
|
||||
}
|
||||
if (!st.dinput && st.mul4) {
|
||||
a_signed = st.mul4->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul4->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA4.extend_u0(n_size, a_signed);
|
||||
st.sigB4.extend_u0(m_size, b_signed);
|
||||
sigA.append(st.sigA4);
|
||||
sigB.append(st.sigB4);
|
||||
}
|
||||
if (st.mul2) {
|
||||
a_signed = st.mul2->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul2->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA2.extend_u0(n_size, a_signed);
|
||||
st.sigB2.extend_u0(m_size, b_signed);
|
||||
sigA.append(st.sigA2);
|
||||
sigB.append(st.sigB2);
|
||||
}
|
||||
if (st.mul3) {
|
||||
a_signed = st.mul3->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul3->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA3.extend_u0(n_size, a_signed);
|
||||
st.sigB3.extend_u0(m_size, b_signed);
|
||||
sigA.append(st.sigA3);
|
||||
sigB.append(st.sigB3);
|
||||
}
|
||||
if (st.dinput && st.mul4) {
|
||||
a_signed = st.mul4->getParam(ID(A_SIGNED)).as_bool();
|
||||
b_signed = st.mul4->getParam(ID(B_SIGNED)).as_bool();
|
||||
st.sigA4.extend_u0(n_size, a_signed);
|
||||
st.sigB4.extend_u0(m_size, b_signed);
|
||||
sigA.append(st.sigA4);
|
||||
sigB.append(st.sigB4);
|
||||
}
|
||||
sigA.extend_u0(4 * n_size);
|
||||
sigB.extend_u0(4 * m_size);
|
||||
|
||||
// reg
|
||||
mode = "00000" + mode + "0000";
|
||||
if (st.level == 1) {
|
||||
if (st.ffA1 && st.ffB1 && !(st.dinput && !st.ffD)) {
|
||||
mode[1] = mode[2] = '1';
|
||||
pm.autoremove(st.ffA1);
|
||||
pm.autoremove(st.ffB1);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffD);
|
||||
}
|
||||
if (st.ffM1 && st.ffD && st.postAdd1) {
|
||||
mode[11] = '1';
|
||||
pm.autoremove(st.ffM1);
|
||||
pm.autoremove(st.ffD);
|
||||
}
|
||||
if (st.ffY1 || (!st.postAdd1 && st.ffM1)) {
|
||||
mode[3] = mode[4] = '1';
|
||||
pm.autoremove(st.ffY1);
|
||||
pm.autoremove(st.ffM1);
|
||||
}
|
||||
}
|
||||
if (st.level == 2) {
|
||||
if (st.ffA1 && st.ffB1 && st.ffA2 && st.ffB2 && st.dinput &&
|
||||
st.ffD) // in reg
|
||||
{
|
||||
mode[1] = mode[2] = '1';
|
||||
pm.autoremove(st.ffA1);
|
||||
pm.autoremove(st.ffB1);
|
||||
pm.autoremove(st.ffA2);
|
||||
pm.autoremove(st.ffB2);
|
||||
pm.autoremove(st.ffD);
|
||||
}
|
||||
if (st.ffA1 && st.ffB1 && st.ffA4 && st.ffB4 && !st.dinput) // in reg
|
||||
{
|
||||
mode[1] = mode[2] = '1';
|
||||
pm.autoremove(st.ffA1);
|
||||
pm.autoremove(st.ffB1);
|
||||
pm.autoremove(st.ffA4);
|
||||
pm.autoremove(st.ffB4);
|
||||
}
|
||||
if (st.ffM1 && ((st.dinput && st.ffM2) || st.ffD)) // mul in reg
|
||||
{
|
||||
mode[11] = '1';
|
||||
pm.autoremove(st.ffM1);
|
||||
pm.autoremove(st.ffD);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffM2);
|
||||
}
|
||||
if (st.ffY1 && st.ffY2 && st.dinput) // mul out reg
|
||||
{
|
||||
mode[12] = '1';
|
||||
pm.autoremove(st.ffY1);
|
||||
pm.autoremove(st.ffY2);
|
||||
} else if (st.ffY1 || st.ffY2) // out reg
|
||||
{
|
||||
mode[3] = mode[4] = '1';
|
||||
pm.autoremove(st.ffY1);
|
||||
pm.autoremove(st.ffY2);
|
||||
}
|
||||
}
|
||||
if (st.ffA1 && st.ffB1 && st.ffA2 && st.ffB2 && st.dinput &&
|
||||
st.ffD) // in reg low
|
||||
{
|
||||
mode[2] = '1';
|
||||
pm.autoremove(st.ffA1);
|
||||
pm.autoremove(st.ffB1);
|
||||
pm.autoremove(st.ffA2);
|
||||
pm.autoremove(st.ffB2);
|
||||
pm.autoremove(st.ffD);
|
||||
}
|
||||
if (st.ffA1 && st.ffB1 && st.ffA4 && st.ffB4 && !st.dinput) // in reg low
|
||||
{
|
||||
mode[2] = '1';
|
||||
pm.autoremove(st.ffA1);
|
||||
pm.autoremove(st.ffB1);
|
||||
pm.autoremove(st.ffA4);
|
||||
pm.autoremove(st.ffB4);
|
||||
}
|
||||
if (st.level == 3) {
|
||||
if (st.ffA3 && st.ffB3 && st.dinput) // in reg high
|
||||
{
|
||||
mode[1] = '1';
|
||||
pm.autoremove(st.ffA3);
|
||||
pm.autoremove(st.ffB3);
|
||||
}
|
||||
if (st.ffA2 && st.ffB2 && !st.dinput) // in reg high
|
||||
{
|
||||
mode[1] = '1';
|
||||
pm.autoremove(st.ffA2);
|
||||
pm.autoremove(st.ffB2);
|
||||
}
|
||||
if (st.ffM1 && st.ffM2 && ((st.dinput && st.ffM3) || st.ffD)) // mul in reg
|
||||
{
|
||||
mode[11] = '1';
|
||||
pm.autoremove(st.ffM1);
|
||||
pm.autoremove(st.ffD);
|
||||
pm.autoremove(st.ffM2);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffM3);
|
||||
}
|
||||
if (st.ffY1 && st.ffY2 && !(st.dinput && !st.ffY3)) // mul out reg
|
||||
{
|
||||
mode[12] = '1';
|
||||
pm.autoremove(st.ffY1);
|
||||
pm.autoremove(st.ffY2);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffY3);
|
||||
} else if (st.ffY2 || st.ffY3) // out reg
|
||||
{
|
||||
mode[3] = mode[4] = '1';
|
||||
pm.autoremove(st.ffY2);
|
||||
pm.autoremove(st.ffY3);
|
||||
}
|
||||
}
|
||||
if (st.level == 4) {
|
||||
if (st.ffA3 && st.ffB3 && st.ffA4 && st.ffB4 && st.dinput) // in reg high
|
||||
{
|
||||
mode[1] = '1';
|
||||
pm.autoremove(st.ffA3);
|
||||
pm.autoremove(st.ffB3);
|
||||
pm.autoremove(st.ffA4);
|
||||
pm.autoremove(st.ffB4);
|
||||
}
|
||||
if (st.ffA2 && st.ffB2 && st.ffA3 && st.ffB3 && !st.dinput) // in reg high
|
||||
{
|
||||
mode[1] = '1';
|
||||
pm.autoremove(st.ffA2);
|
||||
pm.autoremove(st.ffB2);
|
||||
pm.autoremove(st.ffA3);
|
||||
pm.autoremove(st.ffB3);
|
||||
}
|
||||
if (st.ffM1 && st.ffM2 && st.ffM3 &&
|
||||
((st.dinput && st.ffM4) || st.ffD)) // mul in reg
|
||||
{
|
||||
mode[11] = '1';
|
||||
pm.autoremove(st.ffM1);
|
||||
pm.autoremove(st.ffD);
|
||||
pm.autoremove(st.ffM2);
|
||||
pm.autoremove(st.ffM3);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffM4);
|
||||
}
|
||||
if (st.ffY1 && st.ffY2 && st.ffY3 &&
|
||||
!(st.dinput && !st.ffY4)) // mul out reg
|
||||
{
|
||||
mode[12] = '1';
|
||||
pm.autoremove(st.ffY1);
|
||||
pm.autoremove(st.ffY2);
|
||||
pm.autoremove(st.ffY3);
|
||||
if (st.dinput)
|
||||
pm.autoremove(st.ffY4);
|
||||
} else if (st.ffY3 || st.ffY4) // out reg high
|
||||
{
|
||||
if (st.multiout2 || st.multiout3)
|
||||
mode[3] = '1';
|
||||
else
|
||||
mode[3] = mode[4] = '1';
|
||||
pm.autoremove(st.ffY3);
|
||||
pm.autoremove(st.ffY4);
|
||||
}
|
||||
if (st.ffMY) // out reg low
|
||||
{
|
||||
mode[4] = '1';
|
||||
pm.autoremove(st.ffMY);
|
||||
}
|
||||
}
|
||||
|
||||
cell->setPort(RTLIL::escape_id("a_i"), sigA);
|
||||
cell->setPort(RTLIL::escape_id("b_i"), sigB);
|
||||
cell->setPort(RTLIL::escape_id("d_i"), sigD);
|
||||
cell->setPort(RTLIL::escape_id("out_o"), sigY);
|
||||
cell->setPort(RTLIL::escape_id("mode_i"), Const::from_string(mode));
|
||||
if (st.clock != SigBit())
|
||||
cell->setPort(RTLIL::escape_id("clk_i"), st.clock);
|
||||
cell->setPort(RTLIL::escape_id("rst_acc"), RTLIL::SigSpec(0, 1));
|
||||
cell->setPort(RTLIL::escape_id("accsel"), RTLIL::SigSpec(0, 1));
|
||||
cell->setPort(RTLIL::escape_id("cas_g"), RTLIL::SigSpec(0, 1));
|
||||
|
||||
pm.autoremove(st.mul1);
|
||||
pm.autoremove(st.mul2);
|
||||
pm.autoremove(st.mul3);
|
||||
pm.autoremove(st.mul4);
|
||||
pm.autoremove(st.postAdd1);
|
||||
pm.autoremove(st.postAdd2);
|
||||
pm.autoremove(st.postAdd3);
|
||||
pm.autoremove(st.postAdd4);
|
||||
}
|
||||
|
||||
struct RfNewDSP : public Pass {
|
||||
bool show_help;
|
||||
|
||||
RfNewDSP()
|
||||
: Pass("rf_new_dsp",
|
||||
"Extract multiply-add operators and map to new_dsps") {}
|
||||
|
||||
void help() override {
|
||||
log("\n");
|
||||
log(" rf_new_dsp [options] [selection]\n");
|
||||
log("\n");
|
||||
log(" Extract multiply-add operators and map to new_dsps\n");
|
||||
log("\n");
|
||||
log(" -help: show help desk\n");
|
||||
log("\n");
|
||||
log(" -n_size: specify input n size\n");
|
||||
log("\n");
|
||||
log(" -m_size: specify input m size\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void clear_flags() override { show_help = false; }
|
||||
|
||||
void execute(std::vector<std::string> a_Args,
|
||||
RTLIL::Design *a_Design) override {
|
||||
log_header(a_Design, "Executing RF_NEW_DSP pass.\n");
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
if (a_Args[argidx] == "-help") {
|
||||
show_help = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
if (show_help) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
rf_new_dsp_pm pm(module, module->selected_cells());
|
||||
pm.run_rf_new_dsp(rf_new_dsp);
|
||||
}
|
||||
}
|
||||
} RfNewDsp;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
478
techlibs/rapidflex/src/rf_new_dsp.pmg
Normal file
478
techlibs/rapidflex/src/rf_new_dsp.pmg
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
pattern rf_new_dsp
|
||||
|
||||
state <SigBit> clock
|
||||
state <SigSpec> sigA1 sigA2 sigA3 sigA4
|
||||
state <SigSpec> sigB1 sigB2 sigB3 sigB4
|
||||
state <SigSpec> sigD sigY sigY2 sigM
|
||||
|
||||
state <Cell*> ffA1 ffA2 ffA3 ffA4
|
||||
state <Cell*> ffB1 ffB2 ffB3 ffB4
|
||||
state <Cell*> ffD
|
||||
state <Cell*> ffM1 ffM2 ffM3 ffM4
|
||||
state <Cell*> ffY1 ffY2 ffY3 ffY4 ffMY
|
||||
state <Cell*> postAdd1 postAdd2 postAdd3 postAdd4
|
||||
state <Cell*> mul2 mul3 mul4
|
||||
|
||||
state <bool> multiout2 multiout3 dinput y_signed y2_signed
|
||||
state <int> level
|
||||
|
||||
// Variables used for subpatterns
|
||||
state <SigSpec> argQ argD argA argM
|
||||
udata <SigSpec> dffD dffQ addD addY
|
||||
udata <SigBit> dffclock
|
||||
udata <Cell*> dff postadder multiplier
|
||||
state <IdString> postAddAB
|
||||
|
||||
// (1) match multiplier 1
|
||||
match mul1
|
||||
select mul1->type.in($mul)
|
||||
endmatch
|
||||
|
||||
code sigA1 sigB1 sigY multiout2 multiout3 dinput y_signed y2_signed level
|
||||
sigA1 = port(mul1, \A);
|
||||
sigB1 = port(mul1, \B);
|
||||
sigY = port(mul1, \Y);
|
||||
multiout2 = false;
|
||||
multiout3 = false;
|
||||
y_signed = param(mul1, \A_SIGNED).as_bool();
|
||||
dinput = false;
|
||||
level = 1;
|
||||
endcode
|
||||
|
||||
// (2) Match A input register 1
|
||||
code argQ ffA1 sigA1 clock
|
||||
argQ = sigA1;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA1 = dff;
|
||||
clock = dffclock;
|
||||
sigA1 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (3) Match B input register 1
|
||||
code argQ ffB1 sigB1 clock
|
||||
argQ = sigB1;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB1 = dff;
|
||||
clock = dffclock;
|
||||
sigB1 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (4) Match mul output register 1
|
||||
code argD ffM1 sigY clock
|
||||
argD = sigY;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffM1 = dff;
|
||||
clock = dffclock;
|
||||
sigY = dffQ;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (5) Match post adder 1
|
||||
code argA postAdd1 sigY sigD y_signed
|
||||
argA = sigY;
|
||||
subpattern(post_add);
|
||||
if(postadder) {
|
||||
postAdd1 = postadder;
|
||||
sigY = addY;
|
||||
sigD = addD;
|
||||
y_signed = param(postAdd1, \A_SIGNED).as_bool();
|
||||
}
|
||||
endcode
|
||||
|
||||
// (6) Match D input register
|
||||
code argQ ffD sigD clock
|
||||
argQ = sigD;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffD = dff;
|
||||
clock = dffclock;
|
||||
sigD = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (6-1) Match multiplier 4
|
||||
code argM mul4 sigA4 sigB4 sigD level dinput
|
||||
argM = sigD;
|
||||
subpattern(more_mult);
|
||||
if (multiplier) {
|
||||
mul4 = multiplier;
|
||||
sigA4 = port(mul4, \A);
|
||||
sigB4 = port(mul4, \B);
|
||||
level += 1;
|
||||
}
|
||||
else if(postAdd1)
|
||||
{
|
||||
dinput = true;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (7) Match mac output register 1
|
||||
code argD ffY1 sigY clock
|
||||
argD = sigY;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffY1 = dff;
|
||||
clock = dffclock;
|
||||
sigY = dffQ;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (8) Match post adder 2
|
||||
code argA postAdd2 sigM sigY sigD y_signed
|
||||
argA = sigY;
|
||||
subpattern(post_add);
|
||||
if(postadder) {
|
||||
postAdd2 = postadder;
|
||||
sigY = addY;
|
||||
sigM = addD;
|
||||
y_signed = param(postAdd2, \A_SIGNED).as_bool();
|
||||
}
|
||||
endcode
|
||||
|
||||
// (9) Match mul output register 2
|
||||
code argQ ffM2 sigM clock
|
||||
argQ = sigM;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffM2 = dff;
|
||||
clock = dffclock;
|
||||
sigM = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (10) match multiplier 2
|
||||
code argM mul2 sigA2 sigB2 sigM level
|
||||
argM = sigM;
|
||||
subpattern(more_mult);
|
||||
if (multiplier) {
|
||||
mul2 = multiplier;
|
||||
sigA2 = port(mul2, \A);
|
||||
sigB2 = port(mul2, \B);
|
||||
level += 1;
|
||||
}
|
||||
sigM.remove(0, GetSize(sigM));
|
||||
endcode
|
||||
|
||||
// (11) Match A input register 2
|
||||
code argQ ffA2 sigA2 clock
|
||||
argQ = sigA2;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA2 = dff;
|
||||
clock = dffclock;
|
||||
sigA2 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (12) Match B input register 2
|
||||
code argQ ffB2 sigB2 clock
|
||||
argQ = sigB2;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB2 = dff;
|
||||
clock = dffclock;
|
||||
sigB2 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (13) Match mac output register 2
|
||||
code argD ffY2 sigY clock
|
||||
argD = sigY;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffY2 = dff;
|
||||
clock = dffclock;
|
||||
sigY = dffQ;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (14) Match post adder 3
|
||||
code argA postAdd3 sigM sigY sigD y_signed
|
||||
argA = sigY;
|
||||
subpattern(post_add);
|
||||
if(postadder) {
|
||||
postAdd3 = postadder;
|
||||
sigY = addY;
|
||||
sigM = addD;
|
||||
y_signed = param(postAdd3, \A_SIGNED).as_bool();
|
||||
}
|
||||
endcode
|
||||
|
||||
// (15) Match mul output register 3
|
||||
code argQ ffM3 sigM clock
|
||||
argQ = sigM;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffM3 = dff;
|
||||
clock = dffclock;
|
||||
sigM = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (16) match multiplier 3
|
||||
code argM mul3 sigA3 sigB3 level sigM
|
||||
argM = sigM;
|
||||
subpattern(more_mult);
|
||||
if (multiplier) {
|
||||
mul3 = multiplier;
|
||||
sigA3 = port(mul3, \A);
|
||||
sigB3 = port(mul3, \B);
|
||||
level += 1;
|
||||
}
|
||||
sigM.remove(0, GetSize(sigM));
|
||||
endcode
|
||||
|
||||
// (17) Match A input register 3
|
||||
code argQ ffA3 sigA3 clock
|
||||
argQ = sigA3;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA3 = dff;
|
||||
clock = dffclock;
|
||||
sigA3 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (18) Match B input register 3
|
||||
code argQ ffB3 sigB3 clock
|
||||
argQ = sigB3;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB3 = dff;
|
||||
clock = dffclock;
|
||||
sigB3 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (19) Match mac output register 3
|
||||
code argD ffY3 sigY clock
|
||||
argD = sigY;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffY3 = dff;
|
||||
clock = dffclock;
|
||||
sigY = dffQ;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (20) Match post adder 4
|
||||
code argA postAdd4 sigM sigY sigD y_signed
|
||||
argA = sigY;
|
||||
subpattern(post_add);
|
||||
if(postadder) {
|
||||
postAdd4 = postadder;
|
||||
sigY = addY;
|
||||
sigM = addD;
|
||||
y_signed = param(postAdd4, \A_SIGNED).as_bool();
|
||||
}
|
||||
endcode
|
||||
|
||||
// (21) Match mul output register 4
|
||||
code argQ ffM4 sigM clock
|
||||
argQ = sigM;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffM4 = dff;
|
||||
clock = dffclock;
|
||||
sigM = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (22) match multiplier 4
|
||||
code argM mul4 sigA4 sigB4 level sigM
|
||||
argM = sigM;
|
||||
if(!mul4)
|
||||
{
|
||||
subpattern(more_mult);
|
||||
if (multiplier) {
|
||||
mul4 = multiplier;
|
||||
sigA4 = port(mul4, \A);
|
||||
sigB4 = port(mul4, \B);
|
||||
level += 1;
|
||||
}
|
||||
}
|
||||
sigM.remove(0, GetSize(sigM));
|
||||
endcode
|
||||
|
||||
// (23) Match A input register 4
|
||||
code argQ ffA4 sigA4 clock
|
||||
argQ = sigA4;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffA4 = dff;
|
||||
clock = dffclock;
|
||||
sigA4 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (24) Match B input register 4
|
||||
code argQ ffB4 sigB4 clock
|
||||
argQ = sigB4;
|
||||
subpattern(in_dffe);
|
||||
if (dff) {
|
||||
ffB4 = dff;
|
||||
clock = dffclock;
|
||||
sigB4 = dffD;
|
||||
}
|
||||
endcode
|
||||
|
||||
// (25) Match mac output register 4
|
||||
code argD ffY4 sigY clock
|
||||
argD = sigY;
|
||||
subpattern(out_dffe);
|
||||
if (dff) {
|
||||
ffY4 = dff;
|
||||
clock = dffclock;
|
||||
sigY = dffQ;
|
||||
}
|
||||
endcode
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching against input registers
|
||||
|
||||
subpattern in_dffe
|
||||
arg argQ clock
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
if (argQ.empty())
|
||||
reject;
|
||||
for (const auto &c : argQ.chunks())
|
||||
{
|
||||
// Abandon matches when 'Q' is a constant
|
||||
if (!c.wire)
|
||||
reject;
|
||||
// Abandon matches when 'Q' has the keep attribute set
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff, $dffe, $sdff, $sdffe, $adff, $adffe)
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \Q)[offset] === argQ[0]
|
||||
|
||||
// Check that the rest of argQ is present
|
||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||
|
||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
SigSpec Q = port(ff, \Q);
|
||||
dff = ff;
|
||||
dffclock = port(ff, \CLK);
|
||||
dffD = argQ;
|
||||
SigSpec D = port(ff, \D);
|
||||
argQ = Q;
|
||||
dffD.replace(argQ, D); //to.replace(pattern, with). 'to' become 'with' according 'pattern'
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching output registers
|
||||
|
||||
subpattern out_dffe
|
||||
arg argD argQ clock
|
||||
|
||||
code
|
||||
dff = nullptr;
|
||||
for (auto c : argD.chunks())
|
||||
// Abandon matches when 'D' has the keep attribute set
|
||||
if (c.wire->get_bool_attribute(\keep))
|
||||
reject;
|
||||
endcode
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff, $dffe, $sdff, $sdffe)
|
||||
|
||||
slice offset GetSize(port(ff, \D))
|
||||
index <SigBit> port(ff, \D)[offset] === argD[0]
|
||||
|
||||
// Check that the rest of argD is present
|
||||
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||
|
||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
||||
endmatch
|
||||
|
||||
code argQ
|
||||
SigSpec D = port(ff, \D);
|
||||
SigSpec Q = port(ff, \Q);
|
||||
argQ = argD;
|
||||
argQ.replace(D, Q);
|
||||
|
||||
dff = ff;
|
||||
dffQ = argQ;
|
||||
dffclock = port(ff, \CLK);
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching post adder
|
||||
|
||||
subpattern post_add
|
||||
arg argA
|
||||
|
||||
code
|
||||
postadder = nullptr;
|
||||
endcode
|
||||
|
||||
match adder
|
||||
select adder->type.in($add)
|
||||
choice <IdString> AB {\A, \B}
|
||||
select nusers(port(adder, AB)) == 2
|
||||
|
||||
index <SigBit> port(adder, AB)[0] === argA[0]
|
||||
filter GetSize(port(adder, AB)) >= GetSize(argA)
|
||||
filter port(adder, AB).extract(0, GetSize(argA)) == argA
|
||||
|
||||
set postAddAB AB
|
||||
endmatch
|
||||
|
||||
code argA
|
||||
SigSpec A = port(adder, postAddAB);
|
||||
SigSpec D = port(adder, postAddAB == \A ? \B : \A);
|
||||
SigSpec Y = port(adder, \Y);
|
||||
|
||||
postadder = adder;
|
||||
|
||||
addY = argA;
|
||||
addY.replace(A, Y);
|
||||
|
||||
addD = D;
|
||||
endcode
|
||||
|
||||
// #######################
|
||||
|
||||
// Subpattern for matching multiplier
|
||||
|
||||
subpattern more_mult
|
||||
arg argM
|
||||
|
||||
code
|
||||
multiplier = nullptr;
|
||||
endcode
|
||||
|
||||
match mult
|
||||
select mult->type.in($mul)
|
||||
filter GetSize(port(mult, \Y)) <= GetSize(argM)
|
||||
filter port(mult, \Y) == argM.extract(0, GetSize(port(mult, \Y)))
|
||||
endmatch
|
||||
|
||||
code
|
||||
multiplier = mult;
|
||||
endcode
|
||||
526
techlibs/rapidflex/src/synth_rf_alkaid.cc
Normal file
526
techlibs/rapidflex/src/synth_rf_alkaid.cc
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
* Copyright 2020-2024 RapidFlex
|
||||
*/
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
using namespace std;
|
||||
/* Constants for device name */
|
||||
constexpr const char *ALKDC_DNAME = "alkaidC";
|
||||
constexpr const char *ALKDL_DNAME = "alkaidL";
|
||||
constexpr const char *ALKDT_DNAME = "alkaidT";
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#define XSTR(val) #val
|
||||
#define STR(val) XSTR(val)
|
||||
|
||||
#ifndef PASS_NAME
|
||||
#define PASS_NAME synth_rf_alkaid
|
||||
#endif
|
||||
|
||||
struct SynthRapidFlexPass : public ScriptPass {
|
||||
|
||||
SynthRapidFlexPass()
|
||||
: ScriptPass(STR(PASS_NAME), "Synthesis for RapidFlex Alkaid FPGAs") {}
|
||||
|
||||
void help() override {
|
||||
log("\n");
|
||||
log(" %s [options]\n", STR(PASS_NAME));
|
||||
log("This command runs synthesis for RapidFlex Alkaid FPGAs\n");
|
||||
log("\n");
|
||||
log(" -top <module>\n");
|
||||
log(" use the specified module as top module\n");
|
||||
log("\n");
|
||||
log(" -family <family>\n");
|
||||
log(" run synthesis for the specified RapidFlex architecture\n");
|
||||
log(" generate the synthesis netlist for the specified family.\n");
|
||||
log(" supported values:\n");
|
||||
log(" - alkaidL\n");
|
||||
log(" - alkaidT\n");
|
||||
log(" - alkaidC\n");
|
||||
log("\n");
|
||||
log(" -edif <file>\n");
|
||||
log(" write the design to the specified edif file. Writing of an "
|
||||
"output file\n");
|
||||
log(" is omitted if this parameter is not specified.\n");
|
||||
log("\n");
|
||||
log(" -blif <file>\n");
|
||||
log(" write the design to the specified BLIF file. Writing of an "
|
||||
"output file\n");
|
||||
log(" is omitted if this parameter is not specified.\n");
|
||||
log("\n");
|
||||
log(" -verilog <file>\n");
|
||||
log(" write the design to the specified verilog file. Writing of an "
|
||||
"output\n");
|
||||
log(" file is omitted if this parameter is not specified.\n");
|
||||
log("\n");
|
||||
log(" -no_dsp\n");
|
||||
log(" By default use DSP blocks in output netlist.\n");
|
||||
log(" do not use DSP blocks to implement multipliers and associated "
|
||||
"logic\n");
|
||||
log("\n");
|
||||
log(" -no_adder\n");
|
||||
log(" By default use adder cells in output netlist.\n");
|
||||
log(" Specifying this switch turns it off.\n");
|
||||
log("\n");
|
||||
log(" -no_bram\n");
|
||||
log(" By default use Block RAM in output netlist.\n");
|
||||
log(" Specifying this switch turns it off.\n");
|
||||
log("\n");
|
||||
log(" -insert_clock_buffer\n");
|
||||
log(" By default no insertion of clock buffer for output "
|
||||
"netlist.\n");
|
||||
log(" -cell_map_file <file>\n");
|
||||
log(" write the ckbuf into to the specified XML file. Writing of an "
|
||||
"output file\n");
|
||||
log(" Specifying this switch turns it on.\n");
|
||||
log(" -K <int>\n");
|
||||
log(" Specify the input size of LUT when running optimization. If "
|
||||
"not specified, a default value will be applied. Please do not modify "
|
||||
"this parameter except architecture exploration\n");
|
||||
log(" -parse_only\n");
|
||||
log(" Only apply verilog parsing. This is for rewriting purpose.\n");
|
||||
log(" Specifying this switch turns it on.\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
log("\n");
|
||||
log(" -save_block_diagram <file>\n");
|
||||
log(" generate a block diagram and save to the specified file\n");
|
||||
log(" supported formats: dot, png, svg, eps, pdf\n");
|
||||
log("\n");
|
||||
help_script();
|
||||
}
|
||||
|
||||
std::string top_opt, edif_file, blif_file, family, currmodule, verilog_file,
|
||||
cell_map_file, lib_path, block_diagram_file;
|
||||
bool nodsp;
|
||||
bool no_opt;
|
||||
bool abc9;
|
||||
bool inferAdder;
|
||||
bool inferBram;
|
||||
bool show_help;
|
||||
bool insert_clock_buffer;
|
||||
size_t DEFAULT_K = 5;
|
||||
size_t MIN_K = 4;
|
||||
size_t MAX_K = 6;
|
||||
size_t max_lut_size = DEFAULT_K;
|
||||
bool parse_only = false;
|
||||
|
||||
void clear_flags() override {
|
||||
top_opt = "-auto-top";
|
||||
edif_file = "";
|
||||
blif_file = "";
|
||||
cell_map_file = "";
|
||||
verilog_file = "";
|
||||
currmodule = "";
|
||||
family = ALKDL_DNAME;
|
||||
inferAdder = true;
|
||||
inferBram = true;
|
||||
nodsp = false;
|
||||
no_opt = false;
|
||||
abc9 = false;
|
||||
lib_path = "+/rapidflex/";
|
||||
show_help = false;
|
||||
insert_clock_buffer = false;
|
||||
max_lut_size = DEFAULT_K;
|
||||
parse_only = false;
|
||||
block_diagram_file = "";
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
string run_from, run_to;
|
||||
clear_flags();
|
||||
lib_path = design->scratchpad_get_string("rf.lib_path", lib_path);
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-run" && argidx + 1 < args.size()) {
|
||||
size_t pos = args[argidx + 1].find(':');
|
||||
if (pos == std::string::npos) {
|
||||
run_from = args[++argidx];
|
||||
run_to = args[argidx];
|
||||
} else {
|
||||
run_from = args[++argidx].substr(0, pos);
|
||||
run_to = args[argidx].substr(pos + 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top" && argidx + 1 < args.size()) {
|
||||
top_opt = "-top " + args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-edif" && argidx + 1 < args.size()) {
|
||||
edif_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (args[argidx] == "-family" && argidx + 1 < args.size()) {
|
||||
family = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-blif" && argidx + 1 < args.size()) {
|
||||
blif_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cell_map_file" && argidx + 1 < args.size()) {
|
||||
cell_map_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-verilog" && argidx + 1 < args.size()) {
|
||||
verilog_file = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-K" && argidx + 1 < args.size()) {
|
||||
max_lut_size = std::stoi(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-no_dsp") {
|
||||
nodsp = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-no_adder") {
|
||||
inferAdder = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-no_bram") {
|
||||
inferBram = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-no_opt") {
|
||||
no_opt = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-parse_only") {
|
||||
parse_only = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-help") {
|
||||
show_help = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-insert_clock_buffer") {
|
||||
insert_clock_buffer = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-save_block_diagram") {
|
||||
if (argidx + 1 < args.size() && args[argidx + 1][0] != '-') {
|
||||
block_diagram_file = args[++argidx];
|
||||
} else {
|
||||
block_diagram_file = "";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (show_help) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!design->full_selection()) {
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
}
|
||||
/* Pre-check on family name and confirm on selection*/
|
||||
if (family != ALKDL_DNAME && family != ALKDT_DNAME &&
|
||||
family != ALKDC_DNAME) {
|
||||
log_cmd_error("Invalid family specified: '%s'\n", family.c_str());
|
||||
}
|
||||
log("Selected device family: %s\n", family.c_str());
|
||||
/* Force to enable/disable options upon device limits */
|
||||
if (family == ALKDL_DNAME || family == ALKDC_DNAME) {
|
||||
if (!nodsp) {
|
||||
log_warning("Force to disable dsp inference as the selected device "
|
||||
"does not contain dedicated resources\n");
|
||||
nodsp = true;
|
||||
}
|
||||
if (inferBram) {
|
||||
log_warning("Force to disable RAM inference as the selected device "
|
||||
"does not contain dedicated resources\n");
|
||||
inferBram = false;
|
||||
}
|
||||
}
|
||||
/* By default, no opt should be enabled. Throw a warning if not */
|
||||
if (no_opt) {
|
||||
log_warning("Force to disable any optimization, which may cast an "
|
||||
"negative impact on QoR\n");
|
||||
}
|
||||
if (abc9 && design->scratchpad_get_int("abc9.D", 0) == 0) {
|
||||
log_warning("Delay target has not been set via SDC or scratchpad; "
|
||||
"Assuming 1GHz clock.\n");
|
||||
design->scratchpad_set_int("abc9.W",
|
||||
1000); // set interconnet delay as 1ns
|
||||
}
|
||||
/* Sanity checks on max lut size */
|
||||
if (max_lut_size < MIN_K || max_lut_size > MAX_K) {
|
||||
log_cmd_error(
|
||||
"The provided K=%ld is out of the acceptable range [%ld, %ld]!\n",
|
||||
max_lut_size, MIN_K, MAX_K);
|
||||
return;
|
||||
}
|
||||
|
||||
log_header(design, "Executing SYNTH_RAPIDFLEX pass.\n");
|
||||
log_push();
|
||||
|
||||
run_script(design, run_from, run_to);
|
||||
log_pop();
|
||||
}
|
||||
|
||||
void script() override {
|
||||
if (help_mode) {
|
||||
family = "<family>";
|
||||
}
|
||||
|
||||
std::string noDFFArgs;
|
||||
if (check_label("begin")) {
|
||||
std::string family_path = " " + lib_path + family;
|
||||
std::string read_vlog_args;
|
||||
|
||||
// Read simulation library
|
||||
read_vlog_args = family_path + "/cell_sim.v";
|
||||
|
||||
// Use -nomem2reg here to prevent Yosys from complaining about
|
||||
// some block ram cell models. After all the only part of the cells
|
||||
// library required here is cell port definitions plus specify blocks.
|
||||
if (parse_only) {
|
||||
run("read_verilog " + lib_path + "common/cells_sim.v" + read_vlog_args);
|
||||
} else {
|
||||
run("read_verilog -lib -specify -nomem2reg " + lib_path +
|
||||
"common/cells_sim.v" + read_vlog_args);
|
||||
}
|
||||
run("logger -werror \"multiple conflicting drivers\"");
|
||||
run("check");
|
||||
run(stringf("hierarchy -check %s",
|
||||
help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
run("stat");
|
||||
}
|
||||
|
||||
if (check_label("prepare")) {
|
||||
run("proc");
|
||||
run("flatten");
|
||||
if (parse_only) {
|
||||
log("Running parse-only flow. Exit after flattening the design\n");
|
||||
return;
|
||||
}
|
||||
if (help_mode) {
|
||||
run("tribuf -logic");
|
||||
}
|
||||
if (!no_opt) {
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
}
|
||||
run("deminout");
|
||||
if (!no_opt) {
|
||||
run("opt -nodffe");
|
||||
}
|
||||
|
||||
run("check");
|
||||
if (!no_opt) {
|
||||
run("opt -nodffe");
|
||||
run("fsm");
|
||||
run("opt -nodffe");
|
||||
run("wreduce -keepdc");
|
||||
run("peepopt");
|
||||
run("pmuxtree");
|
||||
run("opt_clean");
|
||||
run("share");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_dsp"), "(skip if -no_dsp)") {
|
||||
struct DspParams {
|
||||
size_t a_maxwidth;
|
||||
size_t b_maxwidth;
|
||||
size_t a_minwidth;
|
||||
size_t b_minwidth;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
const std::vector<DspParams> dsp_rules = {
|
||||
{24, 20, 13, 11, "mult_24x20_map"},
|
||||
{12, 10, 2, 2, "mult_12x10_map"},
|
||||
};
|
||||
|
||||
if (help_mode || family == ALKDT_DNAME) {
|
||||
if (help_mode || !nodsp) {
|
||||
run("memory_dff", " (for alkaidT)");
|
||||
if (!no_opt) {
|
||||
run("wreduce t:$mul", " (for alkaidT)");
|
||||
}
|
||||
run("rf_new_dsp");
|
||||
for (const auto &rule : dsp_rules) {
|
||||
run(stringf("techmap -map +/mul2dsp.v "
|
||||
"-map %s/dsp_map.v "
|
||||
"-D DSP_A_MAXWIDTH=%zu -D DSP_B_MAXWIDTH=%zu "
|
||||
"-D DSP_A_MINWIDTH=%zu -D DSP_B_MINWIDTH=%zu "
|
||||
"-D DSP_NAME=%s",
|
||||
std::string(lib_path + family).c_str(), rule.a_maxwidth,
|
||||
rule.b_maxwidth, rule.a_minwidth, rule.b_minwidth,
|
||||
rule.type.c_str()));
|
||||
/* Without the following command, some multiplier may be skipped */
|
||||
run("chtype -set $mul t:$__soft_mul", " (for alkaidT)");
|
||||
}
|
||||
run("select a:mul2dsp", " (for alkaidT)");
|
||||
run("setattr -unset mul2dsp", " (for alkaidT)");
|
||||
if (!no_opt) {
|
||||
run("opt_expr -fine", " (for alkaidT)");
|
||||
run("wreduce", " (for alkaidT)");
|
||||
}
|
||||
run("select -clear", " (for alkaidT)");
|
||||
// Comment out for further development
|
||||
// run("rf_dsp", " (for
|
||||
// alkaidT)");
|
||||
run("chtype -set $mul t:$__soft_mul", " (for alkaidT)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=5");
|
||||
if (!no_opt) {
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
}
|
||||
run("alumacc");
|
||||
run("pmuxtree");
|
||||
if (!no_opt) {
|
||||
run("opt -nodffe");
|
||||
}
|
||||
run("memory -nomap");
|
||||
if (!no_opt) {
|
||||
run("opt_clean");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_bram", "(skip if -no_bram)") &&
|
||||
((help_mode || family == ALKDT_DNAME) && inferBram)) {
|
||||
if (help_mode || family == ALKDT_DNAME) {
|
||||
run("memory_bram -rules " + lib_path + family + "/bram.txt");
|
||||
}
|
||||
/* TODO: Add bram initilization support */
|
||||
run("techmap -map " + lib_path + family + "/bram_map.v");
|
||||
}
|
||||
if (check_label("map_ffram")) {
|
||||
if (!no_opt) {
|
||||
run("opt -fast -mux_undef -undriven -fine -nodffe");
|
||||
}
|
||||
run("memory_map");
|
||||
if (!no_opt) {
|
||||
run("opt -undriven -fine -nodffe");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_gates")) {
|
||||
if (help_mode ||
|
||||
(inferAdder && (family == ALKDL_DNAME || family == ALKDT_DNAME ||
|
||||
family == ALKDC_DNAME))) {
|
||||
run("techmap -map +/techmap.v -map " + lib_path + family +
|
||||
"/arith_map.v",
|
||||
"(unless -no_adder)");
|
||||
} else {
|
||||
run("techmap");
|
||||
}
|
||||
if (!no_opt) {
|
||||
run("opt -fast -nodffe");
|
||||
run("opt_expr");
|
||||
run("opt_merge");
|
||||
run("opt_clean");
|
||||
run("opt -nodffe");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_ffs")) {
|
||||
run("memory");
|
||||
/* TODO: Support shift-register mapping */
|
||||
/* Run 2 times dff mapping incase anything missing */
|
||||
run("dfflegalize -cell $_DFF_?_ 01 -cell $_DFF_???_ 01 -cell $_SDFF_???_ "
|
||||
"01");
|
||||
run("techmap -map " + lib_path + family + "/dff_map.v");
|
||||
run("dfflegalize -cell $_DFF_?_ 01 -cell $_DFF_???_ 01 -cell $_SDFF_???_ "
|
||||
"01");
|
||||
run("techmap -map " + lib_path + family + "/dff_map.v");
|
||||
run("opt_expr -mux_undef");
|
||||
run("simplemap");
|
||||
run("opt_expr");
|
||||
if (!no_opt) {
|
||||
run("opt_merge");
|
||||
run("opt_dff -nodffe");
|
||||
run("opt_clean");
|
||||
run("opt -nodffe");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_luts")) {
|
||||
run("abc -lut " + std::to_string(max_lut_size));
|
||||
/* Map dff and adder again since ABC may generate new gates */
|
||||
run("techmap -map " + lib_path + family + "/dff_map.v");
|
||||
run("techmap -map " + lib_path + family + "/arith_map.v");
|
||||
}
|
||||
|
||||
if (check_label("check")) {
|
||||
run("autoname");
|
||||
run("hierarchy -check");
|
||||
run("stat");
|
||||
run("check -noinit");
|
||||
}
|
||||
|
||||
if (check_label("finalize")) {
|
||||
if (!no_opt) {
|
||||
run("opt_clean -purge");
|
||||
}
|
||||
run("check");
|
||||
}
|
||||
|
||||
if (check_label("insert_clock_buffer", "(if -insert_clock_buffer)")) {
|
||||
if (insert_clock_buffer) {
|
||||
run(stringf("insert_clock_buffer -top %s -cell_map_file %s",
|
||||
top_opt.c_str(), cell_map_file.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("blif", "(if -blif)")) {
|
||||
if (help_mode || !blif_file.empty()) {
|
||||
run(stringf("write_blif -param %s ",
|
||||
help_mode ? "<file-name>" : blif_file.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("verilog", "(if -verilog)")) {
|
||||
if (help_mode || !verilog_file.empty()) {
|
||||
run("write_verilog -noattr -nohex " +
|
||||
(help_mode ? "<file-name>" : verilog_file));
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("save_block_diagram", "(if -save_block_diagram)")) {
|
||||
if (!block_diagram_file.empty()) {
|
||||
size_t dot_pos = block_diagram_file.find_last_of('.');
|
||||
std::string ext, prefix;
|
||||
if (dot_pos != std::string::npos &&
|
||||
dot_pos + 1 < block_diagram_file.length()) {
|
||||
ext = block_diagram_file.substr(dot_pos + 1);
|
||||
prefix = block_diagram_file.substr(0, dot_pos);
|
||||
} else {
|
||||
ext = "dot";
|
||||
prefix = block_diagram_file;
|
||||
}
|
||||
if (ext != "dot" && ext != "png" && ext != "svg" && ext != "eps" &&
|
||||
ext != "pdf") {
|
||||
log_cmd_error("Unsupported block diagram file format: %s. Supported "
|
||||
"formats: dot, png, svg, eps, pdf\n",
|
||||
ext.c_str());
|
||||
return;
|
||||
}
|
||||
run(stringf("show -format %s -prefix %s", ext.c_str(), prefix.c_str()));
|
||||
} else {
|
||||
run("show -format dot");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} SynthRapidFlexPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
Loading…
Add table
Add a link
Reference in a new issue