| `default_nettype none |
| `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 gatecat_fpga_top( |
| 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[3]), .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)); |
| assign cfg_dataclk = io_in[0]; |
| |
| wire cfg_datain; |
| sky130_fd_sc_hd__buf_2 din_buf (.A(io_in[4]), .X(cfg_datain)); |
| |
| localparam W = 5; |
| localparam H = 6; |
| 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) |
| frame_sr <= {cfg_datain, frame_sr[FW-1:1]}; |
| |
| 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 = io_in[0]; |
| wire [6:0] fab_din; |
| sky130_fd_sc_hd__buf_1 din_buf[6:0] (.A(io_in[7:1]), .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 + 1]; |
| if (xx < W-1) assign ri = cell_q[yy][xx+1]; else assign ri = cell_q[yy][xx]; |
| gatecat_logic_cell #(.has_ff(1'b1)) lc_i ( |
| .CLK(fab_clk), |
| .cfg_mode(cfg_mode), |
| .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[5][W-1], cell_q[4][W-1], cell_q[3][W-1], cell_q[H-1]}; |
| |
| |
| endmodule |
| |
| module gatecat_logic_cell ( |
| input CLK, |
| input cfg_mode, |
| 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_mode), |
| .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 |