`default_nettype none | |
// Top level io for this module should stay the same to fit into the scan_wrapper. | |
// The pin connections within the user_module are up to you, | |
// although (if one is present) it is recommended to place a clock on io_in[0]. | |
// This allows use of the internal clock divider if you wish. | |
module user_module_341404507891040852( | |
input [7:0] io_in, | |
output [7:0] io_out | |
); | |
wire cfg_mode, cfg_frameinc, cfg_framestrb, cfg_dataclk; | |
wire [3:0] cfg_sel; | |
sky130_fd_sc_hd__clkbuf_2 mode_clkbuf(.A(io_in[0]), .X(cfg_mode)); | |
sky130_fd_sc_hd__clkbuf_2 frameinc_clkbuf(.A(io_in[1]), .X(cfg_frameinc)); | |
sky130_fd_sc_hd__clkbuf_2 framestrb_clkbuf(.A(io_in[2]), .X(cfg_framestrb)); | |
sky130_fd_sc_hd__clkbuf_4 data_clkbuf(.A(io_in[3]), .X(cfg_dataclk)); | |
sky130_fd_sc_hd__buf_2 sel_buf[3:0] (.A(io_in[7:4]), .X(cfg_sel)); | |
localparam W = 3; | |
localparam H = 5; | |
localparam FW = W * 4; | |
localparam FH = H * 2; | |
reg [$clog2(FH)-1:0] frame_ctr; | |
reg [FW-1:0] frame_sr; | |
always @(posedge cfg_frameinc, negedge cfg_mode) | |
if (~cfg_mode) | |
frame_ctr <= 0; | |
else | |
frame_ctr <= frame_ctr + 1'b1; | |
// avoid a shift register for the frame data because that's the highest hold risk | |
always @(posedge cfg_dataclk, posedge cfg_frameinc) | |
if (cfg_frameinc) | |
frame_sr <= 0; | |
else | |
frame_sr[cfg_sel] = 1'b1; | |
wire [FH-1:0] frame_strb; | |
wire gated_strobe = cfg_mode & cfg_framestrb; | |
generate; | |
genvar ii; | |
for (ii = 0; ii < FH; ii = ii + 1'b1) begin | |
//make sure this is glitch free | |
sky130_fd_sc_hd__nand2_2 cfg_nand (.A(gated_strobe), .B(frame_ctr == ii), .Y(frame_strb[ii])); | |
end | |
endgenerate | |
wire fab_clk; | |
wire [3:0] fab_din; | |
sky130_fd_sc_hd__clkbuf_4 fab_clkbuf(.A(io_in[3]), .X(fab_clk)); | |
sky130_fd_sc_hd__buf_1 din_buf[3:0] (.A(io_in[7:4]), .X(fab_din)); | |
wire [0:W-1] cell_q[0:H-1]; | |
generate | |
genvar xx; | |
genvar yy; | |
for (yy = 0; yy < H; yy = yy + 1'b1) begin: y_c | |
for (xx = 0; xx < W; xx = xx + 1'b1) begin: x_c | |
wire ti, bi, li, ri; | |
if (yy > 0) assign ti = cell_q[yy-1][xx]; else assign ti = fab_din[xx]; | |
if (yy < H-1) assign bi = cell_q[yy+1][xx]; else assign bi = cell_q[yy][xx]; | |
if (xx > 0) assign li = cell_q[yy][xx-1]; else assign li = fab_din[yy >= 4 ? 3 : yy]; | |
if (xx < W-1) assign ri = cell_q[yy][xx+1]; else assign ri = cell_q[yy][xx]; | |
logic_cell_341404507891040852 #(.has_ff(xx[0] ^ yy[0])) lc_i ( | |
.CLK(fab_clk), | |
.cfg_strb(frame_strb[yy * 2 +: 2]), | |
.cfg_data(frame_sr[xx * 4 +: 4]), | |
.T(ti), .B(bi), .L(li),. R(ri), | |
.Q(cell_q[yy][xx]) | |
); | |
end | |
end | |
endgenerate | |
assign io_out = {cell_q[4][W-1], cell_q[3][W-1], cell_q[2][W-1], cell_q[1][W-1], cell_q[0][W-1], cell_q[H-1]}; | |
endmodule | |
module logic_cell_341404507891040852 ( | |
input CLK, | |
input [1:0] cfg_strb, | |
input [3:0] cfg_data, | |
input T, L, R, B, | |
output Q | |
); | |
parameter has_ff = 1'b0; | |
// config storage | |
wire [7:0] cfg; | |
generate | |
genvar ii, jj; | |
for (ii = 0; ii < 2; ii = ii + 1'b1) | |
for (jj = 0; jj < 4; jj = jj + 1'b1) | |
sky130_fd_sc_hd__dlxtn_1 cfg_lat_i ( | |
.D(cfg_data[jj]), | |
.GATE_N(cfg_strb[ii]), | |
.Q(cfg[ii*4 + jj]) | |
); | |
endgenerate | |
wire i0, i1; | |
// I input muxes | |
wire i0a, i0b; | |
sky130_fd_sc_hd__nand2_1 i0muxa0 ( | |
.A(T), .B(cfg[0]), | |
.Y(i0a) | |
); | |
sky130_fd_sc_hd__mux2i_1 i0muxa1 ( | |
.A0(R), .A1(L), .S(cfg[0]), | |
.Y(i0b) | |
); | |
sky130_fd_sc_hd__mux2i_1 i0muxb ( | |
.A0(i0a), .A1(i0b), .S(cfg[1]), | |
.Y(i0) | |
); | |
wire i1a, i1b; | |
sky130_fd_sc_hd__and2_1 i1muxa0 ( | |
.A(cfg[2]), .B(L), | |
.X(i1a) | |
); | |
sky130_fd_sc_hd__mux2i_1 i1muxa1 ( | |
.A0(B), .A1(R), .S(cfg[2]), | |
.Y(i1b) | |
); | |
sky130_fd_sc_hd__mux2i_1 i1muxb ( | |
.A0(i1a), .A1(i1b), .S(cfg[3]), | |
.Y(i1) | |
); | |
// S input mux | |
wire s0s, s0, s0a, s0b; | |
sky130_fd_sc_hd__nand2_1 s0muxa0 ( | |
.A(T), .B(cfg[4]), | |
.Y(s0a) | |
); | |
sky130_fd_sc_hd__mux2i_1 s0muxa1 ( | |
.A0(R), .A1(L), .S(cfg[4]), | |
.Y(s0b) | |
); | |
sky130_fd_sc_hd__mux2i_1 s0muxb ( | |
.A0(s0a), .A1(s0b), .S(cfg[5]), | |
.Y(s0s) | |
); | |
// S invert | |
sky130_fd_sc_hd__xnor2_1 sinv ( | |
.A(s0s), .B(cfg[6]), .Y(s0) | |
); | |
// The logic element | |
wire muxo_n; | |
sky130_fd_sc_hd__mux2i_1 lmux ( | |
.A0(i0), .A1(i1), .S(s0), .Y(muxo_n) | |
); | |
// The DFF | |
generate if (has_ff) begin: dff | |
wire dffo_n; | |
sky130_fd_sc_hd__dfsbp_1 dff( | |
.D(muxo_n), | |
.SET_B(cfg_strb[0]), | |
.CLK(CLK), | |
.Q(dffo_n) | |
); | |
// The final output mux | |
sky130_fd_sc_hd__mux2i_1 ffsel ( | |
.A0(muxo_n), .A1(dffo_n), .S(cfg[7]), .Y(Q) | |
); | |
end else begin | |
sky130_fd_sc_hd__inv_1 linv ( | |
.A(muxo_n), .Y(Q) | |
); | |
end | |
endgenerate | |
endmodule |