actlib_dataflow_neuro/dataflow_neuro/registers.act

410 lines
11 KiB
Plaintext
Raw Normal View History

2022-03-04 11:44:00 +01:00
/*************************************************************************
*
* This file is part of ACT dataflow neuro library
*
* Copyright (c) 2022 University of Groningen - Ole Richter
* Copyright (c) 2022 University of Groningen - Michele Mastella
* Copyright (c) 2022 University of Groningen - Hugh Greatorex
* Copyright (c) 2022 University of Groningen - Madison Cotteret
*
*
* This source describes Open Hardware and is licensed under the CERN-OHL-W v2 or later
*
* You may redistribute and modify this documentation and make products
* using it under the terms of the CERN-OHL-W v2 (https:/cern.ch/cern-ohl).
* This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY
* AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-W v2
* for applicable conditions.
*
* Source location: https://git.web.rug.nl/bics/actlib_dataflow_neuro
*
* As per CERN-OHL-W v2 section 4.1, should You produce hardware based on
* these sources, You must maintain the Source Location visible in its
* documentation.
*
**************************************************************************
*/
2022-03-04 19:04:11 +01:00
2022-03-04 11:44:00 +01:00
import "../../dataflow_neuro/cell_lib_async.act";
import "../../dataflow_neuro/cell_lib_std.act";
import "../../dataflow_neuro/treegates.act";
import "../../dataflow_neuro/primitives.act";
import "../../dataflow_neuro/coders.act";
// import tmpl::dataflow_neuro;
// import tmpl::dataflow_neuro;
import std::channel;
open std::channel;
namespace tmpl {
namespace dataflow_neuro {
/**
* Buffer for use in an A-cell register.
* Basically the same as a normal buffer, except that when out.v goes high,
* in.a goes high too.
* Also, in.a does not wait for out.v to go low to go to low.
* Means have a buffer that completes its Right handshake as soon as out data is valid.
*/
export template<pint N>
defproc buffer_register(avMx1of2<N> in; Mx1of2<N> out; bool? out_v, flush,
reset_B; power supply) {
// BIG TODO
// I HAVE NOT BOTHERED WITH ANY SIGNAL BUFFERING IN HERE YET
//control
bool _en, _reset_BX,_reset_BXX[N];
bool _in_aB;
bool _reset;
INV_X1 reset_inv(.a = reset_B, .y = _reset);
A_2C1N_R_X1 inack_ctl(.c1=_in_aB,.c2=in.v,.n1=out_v,.y=_in_aB,
.pr_B=_reset_BX,.sr_B=_reset_BX,.vdd=supply.vdd,.vss=supply.vss);
INV_X1 inack_inv(.a = _in_aB, .y = in.a, .vdd = supply.vdd, .vss = supply.vss);
// A_1C1P_X1 en_ctl(.c1=in.a,.p1=out.v,.y=_en,
// .vdd=supply.vdd,.vss=supply.vss);
bool _flushB;
INV_X1 flush_inv(.a = flush, .y = _flushB);
// AND2_X1 flush_en(.a = _flushB, .b = _in_aB, .y = _en);
_en = _in_aB;
BUF_X1 reset_buf(.a=reset_B, .y=_reset_BX,.vdd=supply.vdd,.vss=supply.vss);
sigbuf<N> reset_bufarray(.in=_reset_BX, .out=_reset_BXX);
//validity
bool _in_v;
vtree<N> vc(.in=in.d,.out=_in_v,.supply=supply);
BUF_X4 in_v_buf(.a=_in_v, .y=in.v,.vdd=supply.vdd,.vss=supply.vss);
//function
bool _out_a_BX_t[N],_out_a_BX_f[N],_out_a_B,_en_X_t[N],_en_X_f[N];
2022-04-08 17:55:12 +02:00
A_1C2N_SB_X4 f_buf_func[N];
A_1C2N_RB_X4 t_buf_func[N];
sigbuf<N> en_buf_t(.in=_en, .out=_en_X_t, .supply=supply);
sigbuf<N> en_buf_f(.in=_en, .out=_en_X_f, .supply=supply);
// INV_X1 out_a_inv(.a=out.a,.y=_out_a_B, .vss = supply.vss, .vdd = supply.vdd);
// sigbuf<N> out_a_B_buf_f(.in=_out_a_B,.out=_out_a_BX_t);
// sigbuf<N> out_a_B_buf_t(.in=_out_a_B,.out=_out_a_BX_f);
// check if you can also do single var to array connect a=b[N]
// and remove them from the loop
(i:N:
f_buf_func[i].y=out.d[i].f;
t_buf_func[i].y=out.d[i].t;
f_buf_func[i].c1=_flushB;
t_buf_func[i].c1=_flushB;
f_buf_func[i].n2=_en_X_f[i];
t_buf_func[i].n2=_en_X_t[i];
// f_buf_func[i].c2=_out_a_BX_f[i];
// t_buf_func[i].c2=_out_a_BX_t[i];
f_buf_func[i].n1=in.d.d[i].f;
t_buf_func[i].n1=in.d.d[i].t;
f_buf_func[i].vdd=supply.vdd;
t_buf_func[i].vdd=supply.vdd;
f_buf_func[i].vss=supply.vss;
t_buf_func[i].vss=supply.vss;
2022-04-08 17:55:12 +02:00
f_buf_func[i].pr = _reset;
f_buf_func[i].sr = _reset;
t_buf_func[i].pr_B = _reset_BXX[i];
t_buf_func[i].sr_B = _reset_BXX[i];
)
}
/**
2022-04-01 20:45:04 +02:00
* A single register made out of A cells.
* last bit is whether to read or write.
* Currently only handles writing.
*/
export template<pint N>
2022-04-08 12:13:43 +02:00
defproc register_acells(avMx1of2<N+1> in; Mx1of2<N> out;
bool? reset_B; power supply) {
// BIG TODO
// I HAVE NOT BOTHERED WITH ANY SIGNAL BUFFERING IN HERE YET
bool _en2;
bool _w;
bool _out_v, _out_vB;
bool _flush, _flushB;
_w = in.d.d[N].t;
// Buffer
buffer_register<N> buf(.out = out, .out_v = _out_v, .flush = _flush,
.supply = supply, .reset_B = reset_B);
buf.in.v = in.v;
// In ack stuff
INV_X1 in_ack_inv(.a = buf.in.a, .vdd = supply.vdd, .vss = supply.vss);
// To stop in ack going low before en2 has been reset.
2022-04-01 20:45:04 +02:00
A_1C1N_X1 in_ack_safety(.c1 = in_ack_inv.y, .n1 = _en2, .y = in.a,
.vdd = supply.vdd, .vss = supply.vss);
// Out valid tree
vtree<N> out_valid(.in = buf.out, .out = _out_v, .supply = supply);
INV_X2 out_val_inv(.a = _out_v, .y = _out_vB, .vdd = supply.vdd, .vss=supply.vss);
// Control
A_1C1P2N_RB_X1 A_flush(.c1 = _en2, .n1 = _out_v, .n2 = _w, .p1 = _flushB, .y = _flush,
.vdd = supply.vdd, .vss = supply.vss, .sr_B = reset_B, .pr_B = reset_B);
INV_X2 flush_inv(.a = _flush, .y = _flushB, .vdd = supply.vdd, .vss = supply.vss);
2022-04-01 20:45:04 +02:00
A_1C2N_R_X1 A_en2(.c1 = _w, .n1 = _en2, .n2 = _out_vB, .y = _en2,
.pr_B = reset_B, .sr_B = reset_B);
// Pass to let data into the buffer
NOR2_X1 pass(.a = _en2, .b = _flush, .vss = supply.vss, .vdd = supply.vdd);
AND2_X1 gandalf_t[N];
AND2_X1 gandalf_f[N];
(i:0..N-1:
gandalf_t[i].a = in.d.d[i].t;
gandalf_f[i].a = in.d.d[i].f;
gandalf_t[i].b = pass.y;
gandalf_f[i].b = pass.y;
gandalf_t[i].y = buf.in.d.d[i].t;
gandalf_f[i].y = buf.in.d.d[i].f;
gandalf_t[i].vdd = supply.vdd;
gandalf_f[i].vdd = supply.vdd;
gandalf_t[i].vss = supply.vss;
gandalf_f[i].vss = supply.vss;
)
}
2022-04-01 20:45:04 +02:00
/**
* Array of registers made out of A-cells
* params:
* NcW: number of bits in Words to be stored in buffers
* NcA: number of bits in Address
* M: number of registers. M = 2^Nc_addr would be a natural choice.
* Input packets should be
* [-addr-][-word-][r/w]
*/
export template<pint NcA, NcW, M>
2022-04-08 12:13:43 +02:00
defproc register_w_array(avMx1of2<NcA + NcW + 1> in; Mx1of2<NcW> data[M];
2022-04-01 20:45:04 +02:00
bool? reset_B; power supply) {
// BIG TODO
// I HAVE NOT BOTHERED WITH ANY SIGNAL BUFFERING IN HERE YET
vtree<NcA + NcW + 1> input_valid(.in = in.d, .out = in.v,
.supply = supply);
2022-04-01 20:45:04 +02:00
// Address decoder
decoder_dualrail<NcA, M> decoder(.supply = supply);
(i:NcA:
decoder.in.d[i] = in.d.d[i];
)
// OrTree over acks from all registers
ortree<M> ack_ortree(.supply = supply);
// C element handling in ack
A_2C_B_X1 in_ack_Cel(.c1 = ack_ortree.out, .c2 = input_valid.out, .y = in.a,
.vss = supply.vss, .vdd = supply.vdd);
// Write bit selector
bool _w = in.d.d[NcA+NcW].t;
A_2C_B_X1 write_selectors[M];
2022-04-01 20:45:04 +02:00
(i:M:
write_selectors[i].c1 = _w;
write_selectors[i].c2 = decoder.out[i];
2022-04-01 20:45:04 +02:00
write_selectors[i].vdd = supply.vdd;
write_selectors[i].vss = supply.vss;
)
// Registers
2022-04-08 12:13:43 +02:00
register_acells<NcW> registers[M];
2022-04-01 20:45:04 +02:00
TIELO_X1 tielow_writebit_f[M];
(i:M:
// Connect each register to word inputs.
(j:NcW:
registers[i].in.d.d[j] = in.d.d[j + NcA];
)
// Connect the (selected) write bit
registers[i].in.d.d[NcW].t = write_selectors[i].y;
2022-04-01 20:45:04 +02:00
tielow_writebit_f[i].vdd = supply.vdd;
tielow_writebit_f[i].vss = supply.vss;
registers[i].in.d.d[NcW].f = tielow_writebit_f[i].y;
2022-04-01 20:45:04 +02:00
// Connect to ack ortree
registers[i].in.a = ack_ortree.in[i];
// Connect outputs
data[i] = registers[i].out;
registers[i].supply = supply;
registers[i].reset_B = reset_B;
)
}
2022-04-02 18:09:09 +02:00
/**
* Array of registers made out of A-cells
* params:
* NcW: number of bits in Words to be stored in buffers
* NcA: number of bits in Address
* M: number of registers. M = 2^Nc_addr would be a natural choice.
* Input packets should be
* [-addr-][-word-][r/w]
*/
export template<pint NcA, NcW, M>
2022-04-08 12:13:43 +02:00
defproc register_wr_array(avMx1of2<NcA + NcW + 1> in; Mx1of2<NcW> data[M]; avMx1of2<NcA+NcW> out;
2022-04-02 18:09:09 +02:00
bool? reset_B; power supply) {
// BIG TODO
// I HAVE NOT BOTHERED WITH ANY SIGNAL BUFFERING IN HERE YET
// Input valid tree
vtree<NcA + NcW + 1> input_valid(.in = in.d, .out = in.v,
.supply = supply);
// Address decoder
decoder_dualrail<NcA, M> decoder(.supply = supply);
(i:NcA:
decoder.in.d[i] = in.d.d[i];
)
// OrTree over acks from all registers
ortree<M> ack_ortree(.supply = supply);
bool _write_ack;
// C element handling in ack
A_2C_B_X1 in_ack_Cel(.c1 = ack_ortree.out, .c2 = input_valid.out, .y = _write_ack,
.vss = supply.vss, .vdd = supply.vdd);
// Bit to join the acks either from read or write
bool _read_ack;
_read_ack = out.a;
OR2_X1 ack_rw_or(.a = _read_ack, .b = _write_ack, .y = in.a,
.vdd = supply.vdd, .vss = supply.vss);
// Write bit selector
bool _w = in.d.d[NcA+NcW].t;
A_2C_B_X1 write_selectors[M];
(i:M:
write_selectors[i].c1 = _w;
write_selectors[i].c2 = decoder.out[i];
write_selectors[i].vdd = supply.vdd;
write_selectors[i].vss = supply.vss;
)
// Registers
2022-04-08 12:13:43 +02:00
register_acells<NcW> registers[M];
2022-04-02 18:09:09 +02:00
TIELO_X1 tielow_writebit_f[M];
(i:M:
// Connect each register to word inputs.
(j:NcW:
registers[i].in.d.d[j] = in.d.d[j + NcA];
)
// Connect the (selected) write bit
registers[i].in.d.d[NcW].t = write_selectors[i].y;
tielow_writebit_f[i].vdd = supply.vdd;
tielow_writebit_f[i].vss = supply.vss;
registers[i].in.d.d[NcW].f = tielow_writebit_f[i].y;
// Connect to ack ortree
registers[i].in.a = ack_ortree.in[i];
// Connect outputs
data[i] = registers[i].out;
registers[i].supply = supply;
registers[i].reset_B = reset_B;
)
// Read bit selector
bool _r = in.d.d[NcA+NcW].f;
A_2C_B_X1 read_selectors[M];
(i:M:
read_selectors[i].c1 = _r;
read_selectors[i].c2 = decoder.out[i];
read_selectors[i].vdd = supply.vdd;
read_selectors[i].vss = supply.vss;
)
// OrTrees for each output word bit on read
ortree<M> out_ortrees_t[NcW];
ortree<M> out_ortrees_f[NcW];
2022-04-02 18:31:45 +02:00
(i:NcW:
2022-04-02 18:09:09 +02:00
out_ortrees_t[i].out = out.d.d[i+NcA].t;
out_ortrees_f[i].out = out.d.d[i+NcA].f;
out_ortrees_t[i].supply = supply;
out_ortrees_f[i].supply = supply;
)
// ANDs over each reg's data
// and whether it is selected for read.
AND2_X1 and_reads_t[NcW * M];
AND2_X1 and_reads_f[NcW * M];
pint index;
(i:NcW:
(j:M:
2022-04-02 18:31:45 +02:00
index = i + j*NcW;
2022-04-02 18:09:09 +02:00
and_reads_t[index].a = data[j].d[i].t;
and_reads_t[index].b = read_selectors[j].y;
and_reads_f[index].a = data[j].d[i].f;
and_reads_f[index].b = read_selectors[j].y;
and_reads_t[index].y = out_ortrees_t[i].in[j];
and_reads_f[index].y = out_ortrees_f[i].in[j];
and_reads_t[index].vss = supply.vss;
and_reads_t[index].vdd = supply.vdd;
and_reads_f[index].vss = supply.vss;
and_reads_f[index].vdd = supply.vdd;
)
)
// C elements passing address to out on read.
A_2C_B_X1 addr_read_t[NcA];
A_2C_B_X1 addr_read_f[NcA];
(i:NcA:
addr_read_t[i].c1 = in.d.d[i].t;
addr_read_f[i].c1 = in.d.d[i].f;
addr_read_t[i].c2 = _r;
addr_read_f[i].c2 = _r;
addr_read_t[i].y = out.d.d[i].t;
addr_read_f[i].y = out.d.d[i].f;
addr_read_t[i].vdd = supply.vdd;
addr_read_t[i].vss = supply.vss;
addr_read_f[i].vdd = supply.vdd;
addr_read_f[i].vss = supply.vss;
)
}
2022-04-01 20:45:04 +02:00
2022-03-04 11:44:00 +01:00
}}