[xls/contrib] Tutorial: XLS[cc] pipelined loops.
This tutorial is aimed at walking you through the implementation and synthesis
into Verilog of a C++ function containing a pipelined loop. Pipelined loops are
an automatic way of generating stateful logic that can often be more intuitive
and software-like than using explicit state (eg via static
).
C++ Source
Create a file named test_loop.cc
with the following contents.
template<typename T>
using InputChannel = __xls_channel<T, __xls_channel_dir_In>;
template<typename T>
using OutputChannel = __xls_channel<T, __xls_channel_dir_Out>;
class TestBlock {
public:
InputChannel<int> in;
OutputChannel<int> out;
#pragma hls_top
void Run() {
int sum = 0;
#pragma hls_pipeline_unroll yes
for(int i=0;i<4;++i) {
sum += in.read();
}
out.write(sum);
}
};
Generate optimized XLS IR.
Use a combination of xlscc
and opt_main
to generate optimized XLS IR.
$ ./bazel-bin/xls/contrib/xlscc/xlscc test_loop.cc \
--block_from_class TestBlock --block_pb block.pb \
> test_loop.ir
$ ./bazel-bin/xls/tools/opt_main test_loop.ir --inline_procs > test_loop.opt.ir
The --inline_procs
option is necessary to make pipelined loops synthesizable.
Examine the optimized IR
test_loop.opt.ir
should look like this, containing only one proc:
package my_package
file_number 1 "/usr/local/google/home/seanhaskell/tmp/tutorial_loop.cc"
chan in(bits[32], id=0, kind=streaming, ops=receive_only, flow_control=ready_valid, metadata="""""")
chan out(bits[32], id=1, kind=streaming, ops=send_only, flow_control=ready_valid, metadata="""""")
top proc TestBlock_proc(tkn: token, __for_1_proc_state__4: bits[1], __for_1_proc_state__5: bits[32], __for_1_proc_state__6: bits[32], __for_1_proc_activation__1: bits[1], __for_1_ctx_out_receive_holds_activation__1: bits[1], TestBlock_proc_activation__1: bits[1], after_all_186_holds_activation_0__1: bits[1], after_all_186_holds_activation_1__1: bits[1], after_all_84_holds_activation_0__1: bits[1], after_all_84_holds_activation_1__1: bits[1], after_all_84_holds_activation_2__1: bits[1], __for_1_ctx_in_receive_holds_activation__1: bits[1], init={1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0}) {
...
next (after_all.354, __for_1_proc___first_tick_next_state, __for_1_proc_sum_next_state, __for_1_proc_i_next_state, __for_1_ctx_out_receive_activation_out, __for_1_ctx_out_receive_holds_activation_next__1, after_all_84_is_activated, after_all_186_holds_activation_0_next__1, after_all_186_holds_activation_1_next__1, after_all_84_holds_activation_0_next__1, after_all_84_holds_activation_1_next__1, after_all_84_holds_activation_2_next__1, __for_1_ctx_in_receive_holds_activation_next__1)
}
Perform code-generation into a pipelined Verilog block.
In this case, we will generate a single-stage pipeline without input and output flops. This will result in a module with a 32-bit increment adder along with 32-bit of state.
$ ./bazel-bin/xls/tools/codegen_main test_loop.opt.ir \
--generator=pipeline \
--delay_model="sky130" \
--output_verilog_path=xls_counter.v \
--module_name=xls_counter \
--top=TestBlock_proc \
--reset=rst \
--reset_active_low=false \
--reset_asynchronous=false \
--reset_data_path=true \
--pipeline_stages=1 \
--flop_inputs=false \
--flop_outputs=false
Additional XLS[cc] examples.
For developers, it is possible to check if a specific feature is supported by checking translator_proc_test.cc for unit tests.