Skip to content

Commit 6ccf516

Browse files
committed
Support splitting clock signals used as data into separate signals
The packer in particular complains about this type of connectivity, so we fix it during BLIF netlist clean-up.
1 parent 827d8a0 commit 6ccf516

File tree

7 files changed

+236
-24
lines changed

7 files changed

+236
-24
lines changed

vpr/SRC/base/atom_netlist.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,17 @@ AtomPortId AtomNetlist::pin_port (const AtomPinId id) const {
424424
return pin_ports_[id];
425425
}
426426

427-
BitIndex AtomNetlist::pin_port_bit(const AtomPinId id) const {
427+
BitIndex AtomNetlist::pin_port_bit (const AtomPinId id) const {
428428
VTR_ASSERT(valid_pin_id(id));
429429

430430
return pin_port_bits_[id];
431431
}
432432

433+
AtomPortType AtomNetlist::pin_port_type (const AtomPinId id) const {
434+
AtomPortId port = pin_port(id);
435+
return port_type(port);
436+
}
437+
433438
AtomBlockId AtomNetlist::pin_block (const AtomPinId id) const {
434439
//Convenience lookup bypassing the port
435440
VTR_ASSERT(valid_pin_id(id));
@@ -944,6 +949,24 @@ void AtomNetlist::set_pin_is_constant(const AtomPinId pin_id, const bool value)
944949
pin_is_constant_[pin_id] = value;
945950
}
946951

952+
void AtomNetlist::set_pin_net (const AtomPinId pin, AtomPinType pin_type, const AtomNetId net) {
953+
VTR_ASSERT(valid_pin_id(pin));
954+
VTR_ASSERT( (pin_type == AtomPinType::DRIVER && pin_port_type(pin) == AtomPortType::OUTPUT)
955+
|| (pin_type == AtomPinType::SINK && (pin_port_type(pin) == AtomPortType::INPUT || pin_port_type(pin) == AtomPortType::CLOCK)));
956+
957+
AtomNetId orig_net = pin_net(pin);
958+
if(orig_net) {
959+
//Clean up the pin reference on the original net
960+
remove_net_pin(orig_net, pin);
961+
}
962+
963+
//Mark the pin's net
964+
pin_nets_[pin] = net;
965+
966+
//Add the pin to the net
967+
associate_pin_with_net(pin, pin_type, net);
968+
}
969+
947970
void AtomNetlist::remove_block(const AtomBlockId blk_id) {
948971
VTR_ASSERT(valid_block_id(blk_id));
949972

@@ -1763,8 +1786,8 @@ AtomNetlist::AtomStringId AtomNetlist::create_string (const std::string& str) {
17631786
void AtomNetlist::associate_pin_with_net(const AtomPinId pin_id, const AtomPinType type, const AtomNetId net_id) {
17641787
//Add the pin to the net
17651788
if(type == AtomPinType::DRIVER) {
1766-
VTR_ASSERT_MSG(net_pins_[net_id].size() > 0, "Space for net's pin");
1767-
VTR_ASSERT_MSG(net_pins_[net_id][0] == AtomPinId::INVALID(), "No existing net driver");
1789+
VTR_ASSERT_MSG(net_pins_[net_id].size() > 0, "Must be space for net's pin");
1790+
VTR_ASSERT_MSG(net_pins_[net_id][0] == AtomPinId::INVALID(), "Must be no existing net driver");
17681791

17691792
net_pins_[net_id][0] = pin_id; //Set driver
17701793
} else {

vpr/SRC/base/atom_netlist.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,25 +520,28 @@ class AtomNetlist {
520520
* Pins
521521
*/
522522
//Returns the constructed name (derived from block and port) for the specified pin
523-
std::string pin_name (const AtomPinId id) const;
523+
std::string pin_name (const AtomPinId id) const;
524524

525525
//Returns the net associated with the specified pin
526-
AtomNetId pin_net (const AtomPinId id) const;
526+
AtomNetId pin_net (const AtomPinId id) const;
527527

528528
//Returns the pin type of the specified pin
529-
AtomPinType pin_type (const AtomPinId id) const;
529+
AtomPinType pin_type (const AtomPinId id) const;
530530

531531
//Returns the port associated with the specified pin
532-
AtomPortId pin_port (const AtomPinId id) const;
532+
AtomPortId pin_port (const AtomPinId id) const;
533533

534534
//Returns the port bit index associated with the specified pin
535-
BitIndex pin_port_bit (const AtomPinId id) const;
535+
BitIndex pin_port_bit (const AtomPinId id) const;
536+
537+
//Returns the port type associated with the specified pin
538+
AtomPortType pin_port_type (const AtomPinId id) const;
536539

537540
//Returns the block associated with the specified pin
538-
AtomBlockId pin_block (const AtomPinId id) const;
541+
AtomBlockId pin_block (const AtomPinId id) const;
539542

540543
//Returns true if the pin is a constant (i.e. its value never changes)
541-
bool pin_is_constant (const AtomPinId id) const;
544+
bool pin_is_constant (const AtomPinId id) const;
542545

543546
/*
544547
* Nets
@@ -658,6 +661,13 @@ class AtomNetlist {
658661
// value : The boolean value to set the pin_is_constant attribute
659662
void set_pin_is_constant(const AtomPinId pin_id, const bool value);
660663

664+
//Add the specified pin to the specified net as pin_type. Automatically removes
665+
//any previous net connection for this pin.
666+
// pin : The pin to add
667+
// pin_type : The type of the pin (i.e. driver or sink)
668+
// net : The net to add the pin to
669+
void set_pin_net(const AtomPinId pin, AtomPinType pin_type, const AtomNetId net);
670+
661671
/*
662672
* Note: all remove_*() will mark the associated items as invalid, but the items
663673
* will not be removed until compress() is called.

vpr/SRC/base/atom_netlist_utils.c

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
#include "vtr_log.h"
1111

1212
#include "vpr_error.h"
13+
#include "vpr_utils.h"
14+
15+
std::map<AtomNetId,std::vector<AtomPinId>> find_clock_used_as_data_pins(const AtomNetlist& netlist);
16+
17+
AtomBlockId fix_clock_to_data_pins(AtomNetlist& netlist,
18+
const AtomNetId clock_net, const std::vector<AtomPinId>& data_pins,
19+
const t_model* blk_model,
20+
const t_model_ports* model_clock_port, const BitIndex clock_port_bit,
21+
const t_model_ports* model_data_port, const BitIndex data_port_bit);
1322

1423
std::vector<AtomBlockId> identify_buffer_luts(const AtomNetlist& netlist);
1524
bool is_buffer_lut(const AtomNetlist& netlist, const AtomBlockId blk);
@@ -123,9 +132,6 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) {
123132
auto input_ports = netlist.block_input_ports(blk_id);
124133
auto output_ports = netlist.block_output_ports(blk_id);
125134
auto clock_ports = netlist.block_clock_ports(blk_id);
126-
VTR_ASSERT(input_ports.size() == 1);
127-
VTR_ASSERT(output_ports.size() == 1);
128-
VTR_ASSERT(clock_ports.size() == 1);
129135

130136
for(auto ports : {input_ports, output_ports, clock_ports}) {
131137
for(AtomPortId port_id : ports) {
@@ -149,21 +155,35 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) {
149155
}
150156
}
151157

158+
if(d_net.empty()) {
159+
d_net = make_unconn(unconn_count, AtomPinType::SINK);
160+
}
161+
162+
if(q_net.empty()) {
163+
q_net = make_unconn(unconn_count, AtomPinType::DRIVER);
164+
}
165+
166+
if(clk_net.empty()) {
167+
clk_net = make_unconn(unconn_count, AtomPinType::SINK);
168+
}
169+
152170
//Latch type: VPR always assumes rising edge
153171
auto type = "re";
154172

155173
//Latch initial value
156174
int init_val = 3; //Unkown or unspecified
157175
//The initial value is stored as a single value in the truth table
158176
const auto& so_cover = netlist.block_truth_table(blk_id);
159-
VTR_ASSERT(so_cover.size() == 1); //Only one row
160-
VTR_ASSERT(so_cover[0].size() == 1); //Only one column
161-
switch(so_cover[0][0]) {
162-
case vtr::LogicValue::TRUE: init_val = 1; break;
163-
case vtr::LogicValue::FALSE: init_val = 0; break;
164-
case vtr::LogicValue::DONT_CARE: init_val = 2; break;
165-
case vtr::LogicValue::UNKOWN: init_val = 3; break;
166-
default: VTR_ASSERT_MSG(false, "Unrecognzied latch initial state");
177+
if(so_cover.size() == 1) {
178+
VTR_ASSERT(so_cover.size() == 1); //Only one row
179+
VTR_ASSERT(so_cover[0].size() == 1); //Only one column
180+
switch(so_cover[0][0]) {
181+
case vtr::LogicValue::TRUE: init_val = 1; break;
182+
case vtr::LogicValue::FALSE: init_val = 0; break;
183+
case vtr::LogicValue::DONT_CARE: init_val = 2; break;
184+
case vtr::LogicValue::UNKOWN: init_val = 3; break;
185+
default: VTR_ASSERT_MSG(false, "Unrecognzied latch initial state");
186+
}
167187
}
168188

169189
fprintf(f, ".latch %s %s %s %s %d\n", d_net.c_str(), q_net.c_str(), type, clk_net.c_str(), init_val);
@@ -557,6 +577,102 @@ void remove_buffer_lut(AtomNetlist& netlist, AtomBlockId blk) {
557577

558578
}
559579

580+
void fix_clock_to_data_conversions(AtomNetlist& netlist, const t_model* library_models) {
581+
//Some netlists contain clock signals that are also used as data inputs
582+
//
583+
//This function removes these cases by creating a new net whose sinks are the
584+
//'data' sinks of the clock net. This net is then driven by a latch with no
585+
//input data signal, but clocked by the original clock
586+
587+
auto clock_data_pins = find_clock_used_as_data_pins(netlist);
588+
589+
//Use the latch D port to generate the data version fo the clock
590+
const t_model* model = find_model(library_models, "latch");
591+
VTR_ASSERT(model);
592+
const t_model_ports* clock_port = find_model_port(model, "clk");
593+
VTR_ASSERT(clock_port);
594+
BitIndex clock_port_bit = 0;
595+
596+
const t_model_ports* data_port = find_model_port(model, "Q");
597+
VTR_ASSERT(data_port);
598+
BitIndex data_port_bit = 0;
599+
600+
for(auto kv : clock_data_pins) {
601+
AtomNetId clock_net = kv.first;
602+
const std::vector<AtomPinId>& data_pins = kv.second;
603+
604+
vtr::printf_warning(__FILE__, __LINE__, "Clock '%s' drives both clock and data pins, splitting it into separate clock and data nets\n",
605+
netlist.net_name(clock_net).c_str());
606+
607+
fix_clock_to_data_pins(netlist,
608+
clock_net, data_pins,
609+
model,
610+
clock_port, clock_port_bit,
611+
data_port, data_port_bit);
612+
}
613+
}
614+
615+
//Finds all clock sinks which are data ports in the netlist.
616+
//Returns a map of clock_net to the clock's data_sinks
617+
std::map<AtomNetId,std::vector<AtomPinId>> find_clock_used_as_data_pins(const AtomNetlist& netlist) {
618+
std::map<AtomNetId,std::vector<AtomPinId>> clock_data_pins;
619+
620+
auto netlist_clocks = find_netlist_clocks(netlist);
621+
622+
623+
for(AtomNetId clock_net : netlist_clocks) {
624+
for(AtomPinId clock_sink : netlist.net_sinks(clock_net)) {
625+
626+
auto port_type = netlist.pin_port_type(clock_sink);
627+
628+
if(port_type != AtomPortType::CLOCK) {
629+
630+
clock_data_pins[clock_net].push_back(clock_sink);
631+
}
632+
}
633+
}
634+
635+
return clock_data_pins;
636+
}
637+
638+
AtomBlockId fix_clock_to_data_pins(AtomNetlist& netlist,
639+
const AtomNetId clock_net, const std::vector<AtomPinId>& data_pins,
640+
const t_model* blk_model,
641+
const t_model_ports* model_clock_port, const BitIndex clock_port_bit,
642+
const t_model_ports* model_data_port, const BitIndex data_port_bit) {
643+
//Create a new block clocked by clock_net which will drive the 'data' version of the clock
644+
645+
std::string blk_name = "__vpr__" + netlist.net_name(clock_net) + "__as_data__";
646+
VTR_ASSERT_MSG(!netlist.find_block(blk_name), "Failed to create unique clock as data source block name");
647+
648+
//Make the block
649+
AtomBlockId blk = netlist.create_block(blk_name, blk_model);
650+
VTR_ASSERT(blk);
651+
652+
//Connect the clock
653+
AtomPortId clock_port = netlist.create_port(blk, model_clock_port);
654+
netlist.create_pin(clock_port, clock_port_bit, clock_net, AtomPinType::SINK);
655+
656+
//Make the data port
657+
AtomPortId data_port = netlist.create_port(blk, model_data_port);
658+
659+
//Make the net
660+
VTR_ASSERT_MSG(!netlist.find_net(blk_name), "Failed to create unique clock as data net name");
661+
AtomNetId clock_data_net = netlist.create_net(blk_name);
662+
VTR_ASSERT(clock_data_net);
663+
664+
//Create the driver pin
665+
netlist.create_pin(data_port, data_port_bit, clock_data_net, AtomPinType::DRIVER);
666+
667+
//Update all the data pins to connect to clock_data_net instead of the original clock net
668+
for(AtomPinId sink_pin : data_pins) {
669+
670+
netlist.set_pin_net(sink_pin, AtomPinType::SINK, clock_data_net);
671+
}
672+
673+
return blk;
674+
}
675+
560676
bool is_removable_block(const AtomNetlist& netlist, const AtomBlockId blk_id) {
561677
//Any block with no fanout is removable
562678
for(AtomPinId pin_id : netlist.block_output_pins(blk_id)) {
@@ -797,7 +913,7 @@ std::string make_unconn(size_t& unconn_count, AtomPinType /*pin_type*/) {
797913
return std::string("unconn");
798914
}
799915
#else
800-
return std::string("unconn") + std::to_string(unconn_count++);
916+
return std::string("__vpr__unconn") + std::to_string(unconn_count++);
801917
#endif
802918
}
803919

@@ -963,6 +1079,8 @@ std::set<AtomNetId> find_netlist_clocks(const AtomNetlist& netlist) {
9631079
//
9641080
//Since we don't have good information about what pins are clock generators we build a lookup as we go
9651081
for(auto blk_id : netlist.blocks()) {
1082+
if(!blk_id) continue;
1083+
9661084
AtomBlockType type = netlist.block_type(blk_id);
9671085
if(type != AtomBlockType::BLOCK) continue;
9681086

@@ -986,6 +1104,8 @@ std::set<AtomNetId> find_netlist_clocks(const AtomNetlist& netlist) {
9861104

9871105
//Look for connected input clocks
9881106
for(auto pin_id : netlist.block_clock_pins(blk_id)) {
1107+
if(!pin_id) continue;
1108+
9891109
AtomNetId clk_net_id = netlist.pin_net(pin_id);
9901110
VTR_ASSERT(clk_net_id);
9911111

@@ -1001,8 +1121,11 @@ std::set<AtomNetId> find_netlist_clocks(const AtomNetlist& netlist) {
10011121
AtomPortId clk_gen_port = netlist.find_port(blk_id, model_port);
10021122

10031123
for(AtomPinId pin_id : netlist.port_pins(clk_gen_port)) {
1124+
if(!pin_id) continue;
1125+
10041126
AtomNetId clk_net_id = netlist.pin_net(pin_id);
1005-
VTR_ASSERT(clk_net_id);
1127+
if(!clk_net_id) continue;
1128+
10061129
clock_nets.insert(clk_net_id);
10071130
}
10081131
}

vpr/SRC/base/atom_netlist_utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
*/
1616
void absorb_buffer_luts(AtomNetlist& netlist);
1717

18+
/*
19+
* Modifies the netlist to fix cases where a clock is used as data,
20+
* by replacing the data portion of the net with the specified model/port/bit driver
21+
*/
22+
void fix_clock_to_data_conversions(AtomNetlist& netlist, const t_model* library_models);
23+
1824
/*
1925
* Modify the netlist by sweeping away unused nets/blocks/inputs
2026
*/

vpr/SRC/base/read_blif.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ static AtomNetlist read_blif(const char *blif_file,
4646
const t_model *library_models);
4747

4848
static void process_blif(AtomNetlist& netlist,
49+
const t_model *library_models,
4950
bool should_absorb_buffers,
5051
bool should_sweep_dangling_primary_ios,
5152
bool should_sweep_dangling_nets,
@@ -664,6 +665,7 @@ AtomNetlist read_and_process_blif(const char *blif_file,
664665
print_netlist_as_blif("atom_netlist.orig.echo.blif", netlist);
665666

666667
process_blif(netlist,
668+
library_models,
667669
should_absorb_buffers,
668670
should_sweep_dangling_primary_ios,
669671
should_sweep_dangling_nets,
@@ -679,6 +681,7 @@ AtomNetlist read_and_process_blif(const char *blif_file,
679681
}
680682

681683
static void process_blif(AtomNetlist& netlist,
684+
const t_model *library_models,
682685
bool should_absorb_buffers,
683686
bool should_sweep_dangling_primary_ios,
684687
bool should_sweep_dangling_nets,
@@ -712,6 +715,16 @@ static void process_blif(AtomNetlist& netlist,
712715
should_sweep_dangling_nets,
713716
should_sweep_dangling_blocks,
714717
should_sweep_constant_primary_outputs);
718+
719+
//Fix-up cases where a clock is used as a data input
720+
// Currently such connections break the clusterer, so
721+
// we take care of them here. Note that this modification
722+
// likely causes the netlist to no longer be logically equivalent
723+
// to the input
724+
bool should_fix_clock_to_data_conversions = true; //TODO make cmd line option
725+
if(should_fix_clock_to_data_conversions) {
726+
fix_clock_to_data_conversions(netlist, library_models);
727+
}
715728
}
716729

717730
{

0 commit comments

Comments
 (0)