| module scalable_proc # | |
| ( | |
| parameter NUM_PROCESSING_UNITS = 2, // Number of processing units | |
| parameter UART_PRESCALER = 868 // UART prescaler | |
| ) | |
| ( | |
| // Closk & reset | |
| input wire CLK, | |
| input wire RST, | |
| // UART | |
| input wire UART_RX, | |
| output wire UART_TX | |
| ); | |
| // ============================================================================ | |
| // ROM | |
| wire rom_i_stb; | |
| reg [31:0] rom_i_adr; // Fixed to 32-bit. The ROM size must be a power of 2 less than 32 | |
| wire rom_o_stb; | |
| wire [31:0] rom_o_dat; | |
| rom rom | |
| ( | |
| .CLK (CLK), | |
| .RST (RST), | |
| .I_STB (rom_i_stb), | |
| .I_ADR (rom_i_adr), | |
| .O_STB (rom_o_stb), | |
| .O_DAT (rom_o_dat) | |
| ); | |
| // UART transmitt interval | |
| localparam UART_TX_INTERVAL = UART_PRESCALER * 11; // Wait for 10-bits + 1 extra | |
| // ============================================================================ | |
| // Input shift register | |
| localparam SREG_BITS = 32 * NUM_PROCESSING_UNITS; | |
| reg [SREG_BITS-1:0] inp_sreg; | |
| wire inp_sreg_i_stb = rom_o_stb; | |
| wire [31:0] inp_sreg_i_dat = rom_o_dat; | |
| reg inp_sreg_o_stb; | |
| always @(posedge CLK) | |
| if (inp_sreg_i_stb) inp_sreg <= {inp_sreg[SREG_BITS-32-1:0], inp_sreg_i_dat}; | |
| else inp_sreg <= inp_sreg; | |
| always @(posedge CLK or posedge RST) | |
| if (RST) inp_sreg_o_stb <= 1'd0; | |
| else inp_sreg_o_stb <= inp_sreg_i_stb; | |
| // ============================================================================ | |
| // Processing units | |
| reg proc_i_stb; | |
| wire [NUM_PROCESSING_UNITS-1:0] proc_o_stb; | |
| wire [SREG_BITS-1:0] proc_o_dat; | |
| genvar i; | |
| generate for(i=0; i<NUM_PROCESSING_UNITS; i=i+1) begin | |
| processing_unit unit | |
| ( | |
| .CLK (CLK), | |
| .RST (RST), | |
| .I_STB (proc_i_stb), | |
| .I_DAT (inp_sreg[(i+1)*32-1 : i*32]), | |
| .O_STB (proc_o_stb[i]), | |
| .O_DAT (proc_o_dat[(i+1)*32-1 : i*32]) | |
| ); | |
| end endgenerate | |
| // ============================================================================ | |
| // Output shift register | |
| reg [SREG_BITS-1:0] out_sreg; | |
| wire out_sreg_i_ld = proc_o_stb[0]; | |
| wire [SREG_BITS-1:0] out_sreg_i_dat = proc_o_dat; | |
| wire out_sreg_i_sh; | |
| reg out_sreg_o_stb; | |
| wire [3:0] out_sreg_o_dat; | |
| always @(posedge CLK) | |
| if (out_sreg_i_ld) out_sreg <= out_sreg_i_dat; | |
| else if (out_sreg_i_sh) out_sreg <= out_sreg << 4; | |
| else out_sreg <= out_sreg; | |
| assign out_sreg_o_dat = out_sreg[SREG_BITS-1:SREG_BITS-4]; | |
| // DEBUG | |
| always @(posedge CLK) | |
| if (proc_o_stb) $display("%X", {4'dx, proc_o_dat}); | |
| // ============================================================================ | |
| // Control FSM | |
| localparam STATE_INIT = 0; | |
| localparam STATE_LOAD_START = 10; | |
| localparam STATE_LOAD_SHIFT = 11; | |
| localparam STATE_PROC_START = 20; | |
| localparam STATE_PROC_WAIT = 21; | |
| localparam STATE_SEND_START = 30; | |
| localparam STATE_SEND_WAIT = 31; | |
| localparam STATE_SEND_DELIM = 32; | |
| integer fsm; | |
| reg [32:0] fsm_cnt; | |
| wire fsm_pulse; | |
| reg [32:0] fsm_pulse_cnt; | |
| // fsm | |
| always @(posedge CLK or posedge RST) | |
| if (RST) fsm <= STATE_INIT; | |
| else case(fsm) | |
| STATE_INIT: fsm <= STATE_LOAD_START; | |
| STATE_LOAD_START: fsm <= STATE_LOAD_SHIFT; | |
| STATE_LOAD_SHIFT: fsm <= (fsm_cnt[32]) ? STATE_PROC_START : fsm; | |
| STATE_PROC_START: fsm <= STATE_PROC_WAIT; | |
| STATE_PROC_WAIT: fsm <= (proc_o_stb[0]) ? STATE_SEND_START : fsm; | |
| STATE_SEND_START: fsm <= STATE_SEND_WAIT; | |
| STATE_SEND_WAIT: if (fsm_pulse) fsm <= (fsm_cnt[32]) ? STATE_SEND_DELIM : fsm; | |
| STATE_SEND_DELIM: if (fsm_pulse) fsm <= STATE_INIT; | |
| endcase | |
| // fsm_cnt | |
| always @(posedge CLK) | |
| case (fsm) | |
| STATE_LOAD_START: fsm_cnt <= NUM_PROCESSING_UNITS - 2; // 32-bits per shift | |
| STATE_SEND_START: fsm_cnt <= NUM_PROCESSING_UNITS * (32 / 4) - 2; // 4-bits per shift | |
| STATE_SEND_WAIT: if (fsm_pulse) fsm_cnt <= (fsm_cnt[32]) ? fsm_cnt : (fsm_cnt - 1); | |
| default: fsm_cnt <= (fsm_cnt[32]) ? fsm_cnt : (fsm_cnt - 1); | |
| endcase | |
| // fsm_pulse_cnt | |
| always @(posedge CLK or posedge RST) | |
| if (RST) fsm_pulse_cnt <= UART_TX_INTERVAL - 2; | |
| else fsm_pulse_cnt <= (fsm_pulse_cnt[31]) ? UART_TX_INTERVAL - 2 : fsm_pulse_cnt - 1; | |
| assign fsm_pulse = fsm_pulse_cnt[31]; | |
| // ============================================================================ | |
| // Control signals | |
| wire uart_i_stb; | |
| wire [4:0] uart_i_dat; | |
| assign rom_i_stb = (fsm == STATE_LOAD_SHIFT); | |
| always @(posedge CLK or posedge RST) | |
| if (RST) rom_i_adr <= 0; | |
| else case(fsm) | |
| STATE_LOAD_SHIFT: rom_i_adr <= rom_i_adr + 1; | |
| default: rom_i_adr <= rom_i_adr; | |
| endcase | |
| always @(posedge CLK or posedge RST) | |
| if (RST) proc_i_stb <= 0; | |
| else proc_i_stb <= (fsm == STATE_PROC_START); | |
| assign out_sreg_i_sh = (fsm == STATE_SEND_WAIT) && fsm_pulse; | |
| assign uart_i_stb = fsm_pulse && ((fsm == STATE_SEND_WAIT) || | |
| (fsm == STATE_SEND_DELIM)); | |
| assign uart_i_dat = (fsm == STATE_SEND_WAIT) ? {1'd0, out_sreg_o_dat} : | |
| (fsm == STATE_SEND_DELIM) ? {1'd1, 4'd0} : | |
| {1'd1, 4'd0}; | |
| // ============================================================================ | |
| // UART string generator | |
| reg uart_x_stb; | |
| reg [7:0] uart_x_dat; | |
| always @(posedge CLK) | |
| case (uart_i_dat) | |
| 5'h00: uart_x_dat <= "0"; | |
| 5'h01: uart_x_dat <= "1"; | |
| 5'h02: uart_x_dat <= "2"; | |
| 5'h03: uart_x_dat <= "3"; | |
| 5'h04: uart_x_dat <= "4"; | |
| 5'h05: uart_x_dat <= "5"; | |
| 5'h06: uart_x_dat <= "6"; | |
| 5'h07: uart_x_dat <= "7"; | |
| 5'h08: uart_x_dat <= "8"; | |
| 5'h09: uart_x_dat <= "9"; | |
| 5'h0A: uart_x_dat <= "A"; | |
| 5'h0B: uart_x_dat <= "B"; | |
| 5'h0C: uart_x_dat <= "C"; | |
| 5'h0D: uart_x_dat <= "D"; | |
| 5'h0E: uart_x_dat <= "E"; | |
| 5'h0F: uart_x_dat <= "F"; | |
| 5'h10: uart_x_dat <= "\n"; | |
| default: uart_x_dat <= " "; | |
| endcase | |
| always @(posedge CLK or posedge RST) | |
| if (RST) uart_x_stb <= 1'd0; | |
| else uart_x_stb <= uart_i_stb; | |
| // ============================================================================ | |
| // UART | |
| // Baudrate prescaler initializer | |
| reg [7:0] reg_div_we_sr; | |
| wire reg_div_we; | |
| always @(posedge CLK or posedge RST) | |
| if (RST) reg_div_we_sr <= 8'h01; | |
| else reg_div_we_sr <= {reg_div_we_sr[6:0], 1'd0}; | |
| assign reg_div_we = reg_div_we_sr[7]; | |
| // The UART | |
| simpleuart uart | |
| ( | |
| .clk (CLK), | |
| .resetn (!RST), | |
| .ser_rx (UART_RX), | |
| .ser_tx (UART_TX), | |
| .reg_div_we ({reg_div_we, reg_div_we, reg_div_we, reg_div_we}), | |
| .reg_div_di (UART_PRESCALER), | |
| .reg_div_do (), | |
| .reg_dat_we (uart_x_stb), | |
| .reg_dat_re (1'd0), | |
| .reg_dat_di ({24'd0, uart_x_dat}), | |
| .reg_dat_do (), | |
| .reg_dat_wait () | |
| ); | |
| // Debug | |
| always @(posedge CLK) | |
| if (uart_x_stb) | |
| $display("%c", uart_x_dat); | |
| endmodule | |