Skip to content

Commit de9dd39

Browse files
committed
dualedge support (incl async reset)
1 parent f449089 commit de9dd39

File tree

4 files changed

+186
-12
lines changed

4 files changed

+186
-12
lines changed

src/async_pattern.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ void TimingPatternInterpretor::handle_always(const ast::ProceduralBlockSymbol &s
7979
break;
8080

8181
case ast::EdgeKind::BothEdges:
82-
issuer.add_diag(diag::BothEdgesUnsupported, sigev.sourceRange);
82+
triggers.push_back(&sigev);
8383
break;
8484
}
8585
}

src/slang_frontend.cc

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,13 +2088,52 @@ struct PopulateNetlist : public TimingPatternInterpretor, public ast::ASTVisitor
20882088

20892089
RTLIL::Cell *cell;
20902090
if (aloads.empty()) {
2091+
if (clock.edge == ast::EdgeKind::BothEdges) {
2092+
// dual-edge: synthesize via posedge+negedge FFs and mux (ref: https://vlsi-soc.blogspot.com/2013/06/dual-edge-triggered-flip-flop.html)
2093+
for (auto [named_chunk, name] : generate_subfield_names(driven_chunk, type)) {
2094+
std::string var_name = RTLIL::unescape_id(netlist.id(*named_chunk.variable.get_symbol()));
2095+
2096+
// intermediaries that dont connect directly to output
2097+
std::string pos_wire_name = "$driver$pos$q$" + var_name + name;
2098+
RTLIL::Wire *pos_q = netlist.canvas->addWire(netlist.canvas->uniquify(pos_wire_name), named_chunk.bitwidth());
2099+
2100+
std::string neg_wire_name = "$driver$neg$q$" + var_name + name;
2101+
RTLIL::Wire *neg_q = netlist.canvas->addWire(netlist.canvas->uniquify(neg_wire_name), named_chunk.bitwidth());
2102+
2103+
std::string pos_name = "$driver$pos$" + var_name + name;
2104+
RTLIL::Cell *pos_ff = netlist.canvas->addDff(netlist.canvas->uniquify(pos_name),
2105+
timing.triggers[0].signal,
2106+
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2107+
pos_q,
2108+
/*edge_polarity=*/true);
2109+
transfer_attrs(symbol, pos_ff);
2110+
2111+
std::string neg_name = "$driver$neg$" + var_name + name;
2112+
RTLIL::Cell *neg_ff = netlist.canvas->addDff(netlist.canvas->uniquify(neg_name),
2113+
timing.triggers[0].signal,
2114+
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2115+
neg_q,
2116+
/*edge_polarity=*/false);
2117+
transfer_attrs(symbol, neg_ff);
2118+
2119+
// mux behavior: A=neg_q (output when clk=0), B=pos_q (output when clk=1), S=clk
2120+
std::string mux_name = "$driver$mux$" + var_name + name;
2121+
RTLIL::Cell *mux = netlist.canvas->addMux(netlist.canvas->uniquify(mux_name),
2122+
/*A=*/neg_q,
2123+
/*B=*/pos_q,
2124+
/*S=*/timing.triggers[0].signal,
2125+
/*Y=*/netlist.convert_static(named_chunk));
2126+
transfer_attrs(symbol, mux);
2127+
}
2128+
} else {
20912129
for (auto [named_chunk, name] : generate_subfield_names(driven_chunk, type)) {
20922130
cell = netlist.canvas->addDff(netlist.canvas->uniquify("$driver$" + RTLIL::unescape_id(netlist.id(*named_chunk.variable.get_symbol())) + name),
20932131
timing.triggers[0].signal,
20942132
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
20952133
netlist.convert_static(named_chunk),
20962134
timing.triggers[0].edge_polarity);
20972135
transfer_attrs(symbol, cell);
2136+
}
20982137
}
20992138
} else if (aloads.size() == 1) {
21002139
VariableBits aldff_q;
@@ -2110,17 +2149,64 @@ struct PopulateNetlist : public TimingPatternInterpretor, public ast::ASTVisitor
21102149
}
21112150

21122151
if (!aldff_q.empty()) {
2113-
for (auto driven_chunk2 : aldff_q.chunks())
2114-
for (auto [named_chunk, name] : generate_subfield_names(driven_chunk2, type)) {
2115-
cell = netlist.canvas->addAldff(netlist.canvas->uniquify("$driver$" + RTLIL::unescape_id(netlist.id(*named_chunk.variable.get_symbol())) + name),
2116-
timing.triggers[0].signal,
2117-
aloads[0].trigger,
2118-
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2119-
netlist.convert_static(named_chunk),
2120-
aloads[0].values.evaluate(netlist, named_chunk),
2121-
timing.triggers[0].edge_polarity,
2122-
aloads[0].trigger_polarity);
2123-
transfer_attrs(symbol, cell);
2152+
if (clock.edge == ast::EdgeKind::BothEdges) {
2153+
for (auto driven_chunk2 : aldff_q.chunks())
2154+
for (auto [named_chunk, name] : generate_subfield_names(driven_chunk2, type)) {
2155+
std::string var_name = RTLIL::unescape_id(netlist.id(*named_chunk.variable.get_symbol()));
2156+
2157+
// intermediaries
2158+
std::string pos_wire_name = "$driver$pos$q$" + var_name + name;
2159+
RTLIL::Wire *pos_q = netlist.canvas->addWire(netlist.canvas->uniquify(pos_wire_name), named_chunk.bitwidth());
2160+
2161+
std::string neg_wire_name = "$driver$neg$q$" + var_name + name;
2162+
RTLIL::Wire *neg_q = netlist.canvas->addWire(netlist.canvas->uniquify(neg_wire_name), named_chunk.bitwidth());
2163+
2164+
// posedge aldff
2165+
std::string pos_name = "$driver$pos$" + var_name + name;
2166+
RTLIL::Cell *pos_ff = netlist.canvas->addAldff(netlist.canvas->uniquify(pos_name),
2167+
timing.triggers[0].signal,
2168+
aloads[0].trigger,
2169+
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2170+
pos_q,
2171+
aloads[0].values.evaluate(netlist, named_chunk),
2172+
/*clk_polarity=*/true,
2173+
aloads[0].trigger_polarity);
2174+
transfer_attrs(symbol, pos_ff);
2175+
2176+
// negedge aldff
2177+
std::string neg_name = "$driver$neg$" + var_name + name;
2178+
RTLIL::Cell *neg_ff = netlist.canvas->addAldff(netlist.canvas->uniquify(neg_name),
2179+
timing.triggers[0].signal,
2180+
aloads[0].trigger,
2181+
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2182+
neg_q,
2183+
aloads[0].values.evaluate(netlist, named_chunk),
2184+
/*clk_polarity=*/false,
2185+
aloads[0].trigger_polarity);
2186+
transfer_attrs(symbol, neg_ff);
2187+
2188+
// mux behavior: A=neg_q (output when clk=0), B=pos_q (output when clk=1), S=clk
2189+
std::string mux_name = "$driver$mux$" + var_name + name;
2190+
RTLIL::Cell *mux = netlist.canvas->addMux(netlist.canvas->uniquify(mux_name),
2191+
/*A=*/neg_q,
2192+
/*B=*/pos_q,
2193+
/*S=*/timing.triggers[0].signal,
2194+
/*Y=*/netlist.convert_static(named_chunk));
2195+
transfer_attrs(symbol, mux);
2196+
}
2197+
} else {
2198+
for (auto driven_chunk2 : aldff_q.chunks())
2199+
for (auto [named_chunk, name] : generate_subfield_names(driven_chunk2, type)) {
2200+
cell = netlist.canvas->addAldff(netlist.canvas->uniquify("$driver$" + RTLIL::unescape_id(netlist.id(*named_chunk.variable.get_symbol())) + name),
2201+
timing.triggers[0].signal,
2202+
aloads[0].trigger,
2203+
assigned.extract(named_chunk.base - driven_chunk.base, named_chunk.bitwidth()),
2204+
netlist.convert_static(named_chunk),
2205+
aloads[0].values.evaluate(netlist, named_chunk),
2206+
timing.triggers[0].edge_polarity,
2207+
aloads[0].trigger_polarity);
2208+
transfer_attrs(symbol, cell);
2209+
}
21242210
}
21252211
}
21262212

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ set(ALL_TESTS
5757
various/wait_test.ys
5858
various/expect_test.ys
5959
various/program_test.ys
60+
various/dualedge.ys
6061
)
6162

6263
foreach(test_script ${ALL_TESTS})

tests/various/dualedge.ys

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
read_slang <<EOF
2+
module dualedge(input logic clk, input logic d, output logic q);
3+
always_ff @(edge clk)
4+
q <= d;
5+
endmodule
6+
EOF
7+
8+
select -assert-count 2 t:$dff # one for negedge, one for posedge
9+
select -assert-count 1 t:$mux # should have 1 mux to combine outputs
10+
11+
select -assert-count 1 t:$dff r:CLK_POLARITY=1 %i
12+
select -assert-count 1 t:$dff r:CLK_POLARITY=0 %i
13+
14+
select -assert-count 2 t:$dff %ci:+[CLK] w:clk %d # redundancy but nice to check
15+
select -assert-count 2 t:$dff %ci:+[D] w:d %d
16+
select -assert-count 1 t:$mux %ci:+[S] w:clk %d
17+
select -assert-count 1 t:$mux %co:+[Y] w:q %d
18+
19+
design -reset
20+
read_slang <<EOF
21+
module mixed_edges(input logic clk, input logic d, output logic q_pos, output logic q_neg, output logic q_dual);
22+
always_ff @(posedge clk)
23+
q_pos <= d;
24+
25+
always_ff @(negedge clk)
26+
q_neg <= d;
27+
28+
always_ff @(edge clk)
29+
q_dual <= d;
30+
endmodule
31+
EOF
32+
33+
select -assert-count 4 t:$dff
34+
select -assert-count 1 t:$mux
35+
select -assert-count 2 t:$dff r:CLK_POLARITY=1 %i
36+
select -assert-count 2 t:$dff r:CLK_POLARITY=0 %i
37+
38+
39+
design -reset
40+
read_slang <<EOF
41+
module dualedge_async_reset(
42+
input logic clk,
43+
input logic rst_n,
44+
input logic d,
45+
output logic q
46+
);
47+
always_ff @(edge clk or negedge rst_n) begin
48+
if (!rst_n)
49+
q <= 1'b0;
50+
else
51+
q <= d;
52+
end
53+
endmodule
54+
EOF
55+
56+
select -assert-count 2 t:$aldff
57+
select -assert-count 1 t:$mux
58+
select -assert-count 1 t:$aldff r:CLK_POLARITY=1 %i
59+
select -assert-count 1 t:$aldff r:CLK_POLARITY=0 %i
60+
select -assert-count 2 t:$aldff r:ALOAD_POLARITY=0 %i
61+
select -assert-count 2 t:$aldff %ci:+[CLK] w:clk %d
62+
select -assert-count 2 t:$aldff %ci:+[D] w:d %d
63+
select -assert-count 2 t:$aldff %ci:+[ALOAD] w:rst_n %d # asyc load signal as rst_n
64+
select -assert-count 1 t:$mux %ci:+[S] w:clk %d
65+
select -assert-count 1 t:$mux %co:+[Y] w:q %d
66+
67+
design -reset
68+
read_slang <<EOF
69+
module dualedge_multibit(
70+
input logic clk,
71+
input logic [7:0] d,
72+
output logic [7:0] q
73+
);
74+
always_ff @(edge clk)
75+
q <= d;
76+
endmodule
77+
EOF
78+
79+
select -assert-count 2 t:$dff
80+
select -assert-count 1 t:$mux
81+
82+
select -assert-count 2 t:$dff r:WIDTH=8 %i
83+
84+
select -assert-count 1 t:$dff r:CLK_POLARITY=1 %i
85+
select -assert-count 1 t:$dff r:CLK_POLARITY=0 %i
86+
87+
select -assert-count 1 t:$mux r:WIDTH=8 %i

0 commit comments

Comments
 (0)