| /////////////////////////////////////////////////////////////////////////// | |
| // M0 - 16-bit serial SUBLEQ processor | |
| // | |
| // Copyright 2022 William Moyes | |
| // | |
| // The M0 is a 16-bit, bit serial microprocessor based upon the SUBLEQ | |
| // architecture. The only external devices needed for operation are a SPI | |
| // RAM, SPI ROM, and clock source. The entire ROM and RAM are available for | |
| // user code. All registers and logic are contained within the M0 itself. | |
| // A transmit UART is included for serial output. | |
| // | |
| // See README.md at https://github.com/moyesw/TT02-M0/blob/main/README.md | |
| // for more information on the M0 architecture. | |
| // | |
| // The M0 microarchitecture | |
| // -------------------------- | |
| // PC - Program counter shift register | |
| // ADR - Address shift register | |
| // TMP - Temporary shift register | |
| // | |
| // The M0 has a six phase exeuction sequence. Each phase performs | |
| // one 16-bit access to the SPI bus: | |
| // Phase 0: ADR <-- mem[PC++] | |
| // Phase 1: TMP <-- mem[ADR] | |
| // Phase 2: ADR <-- mem[PC++] | |
| // Phase 3: TMP <-- mem[ADR] - TMP ;checks if result <= 0 | |
| // Phase 4: mem[ADR] <-- TMP | |
| // Phase 5: PC <-- mem[PC++] or PC++ | |
| // | |
| `default_nettype none | |
| `timescale 100us/10ps | |
| /////////////////////////////////////////////////////////////////////////// | |
| // SPI Controller | |
| // | |
| // 16-bit Address + 16-bit Data controller and timing generator | |
| // | |
| module SPIController ( | |
| // System Interfaces | |
| input wire clk, | |
| input wire rst, | |
| // SPI Bus Interfaces | |
| output reg CS0, | |
| output reg CS1, | |
| output reg SPICLK, | |
| output reg MOSI, | |
| input wire MISO, | |
| // Input Signals | |
| input wire Addr15, // Sampled on Phase 01 | |
| input wire Read_notWrite, // Sampled on Phase 16 | |
| input wire Addr, // Sampled on Phase 18[bit0/LSB], 20[bit1], ..., 44[bit13], 46[bit14/MSB], bit 15 not sampled (see Addr15) | |
| input wire Data, // Sampled on Phase 50[bit0/LSB], 52[bit1], ..., 78[bit14], 80[bit15/MSB] | |
| // Timing Output Signals | |
| output reg ShiftAddr, // Asserted when the Address should be shifted | |
| output reg ShiftDataRead, // Asserted when the data register collecting data read from memory should be shifted | |
| output reg ShiftDataWrite, // Asserted when the data regsiter providing data to be written to memory should be shifted | |
| output reg PresetCarry, // Asserted the clock before data motion starts | |
| output reg EndOfPhase, // | |
| output reg PrepOutput | |
| ); | |
| // SPI sequencer | |
| reg [6:0] SPIphase; | |
| always @(posedge clk) begin | |
| if (rst) | |
| SPIphase <= 0; | |
| else if (SPIphase == 83) | |
| SPIphase <= 0; | |
| else | |
| SPIphase <= SPIphase + 1; | |
| end | |
| // SPI bus signal generator | |
| always @(posedge clk) begin | |
| if (SPIphase <= 1) begin | |
| CS0 <= 1; | |
| CS1 <= 1; | |
| SPICLK <= 0; | |
| MOSI <= 0; | |
| end else begin | |
| CS0 <= CSreg; | |
| CS1 <= !CSreg; | |
| if (SPIphase <= 81) | |
| SPICLK <= SPIphase[0]; | |
| else | |
| SPICLK <= 0; | |
| if (SPIphase <= 13) | |
| MOSI <= 0; | |
| else if (SPIphase <= 15) | |
| MOSI <= 1; | |
| else if (SPIphase <= 17) begin | |
| if (SPIphase[0] == 0) | |
| MOSI <= Read_notWrite; | |
| end else if (SPIphase <= 47) begin | |
| if (SPIphase[0] == 0) | |
| MOSI <= Addr; | |
| end else if (SPIphase <= 49) | |
| MOSI <= 0; | |
| else begin | |
| if (Read_notWrite) | |
| MOSI <= 0; | |
| else begin | |
| if (SPIphase[0] == 0) | |
| MOSI <= Data; | |
| end | |
| end | |
| end | |
| end | |
| // Generate Address Shift Enable Signals | |
| always @(posedge clk) begin | |
| ShiftAddr <= ((SPIphase >= 18) && (SPIphase <= 48) && (SPIphase[0] == 0)); | |
| ShiftDataRead <= ((SPIphase >= 51) && (SPIphase <= 81) && (SPIphase[0] == 1) && Read_notWrite); | |
| ShiftDataWrite <= ((SPIphase >= 50) && (SPIphase <= 80) && (SPIphase[0] == 0) && !Read_notWrite); | |
| PresetCarry <= (SPIphase == 17); | |
| EndOfPhase <= (SPIphase == 83); | |
| PrepOutput <= (SPIphase == 49); | |
| end | |
| reg CSreg; | |
| always @(posedge clk) begin | |
| if (SPIphase == 1) | |
| CSreg <= Addr15; | |
| end | |
| endmodule | |
| /////////////////////////////////////////////////////////////////////////// | |
| // M0 top level | |
| // | |
| module moyes0_top_module ( | |
| input [7:0] io_in, | |
| output [7:0] io_out | |
| ); | |
| // --- ASIC Inputs --- | |
| wire clk = io_in[0]; // System clock (~6000 Hz) | |
| wire rst = io_in[1]; // Reset line, active high | |
| wire spi_miso= io_in[2]; // SPI bus, ASIC input, target output | |
| wire uart_rx = io_in[3]; // Serial port, ASIC Receive | |
| wire in4 = io_in[4]; | |
| wire in5 = io_in[5]; | |
| wire in6 = io_in[6]; | |
| wire in7 = io_in[7]; | |
| // --- ASIC Outputs --- | |
| wire spi_cs0; | |
| wire spi_cs1; | |
| wire spi_clk; | |
| wire spi_mosi; | |
| wire uart_tx; | |
| wire out5; | |
| wire out6; | |
| wire out7; | |
| wire [7:0] io_out; | |
| assign io_out[0] = spi_cs0; // SPI bus, Chip Select for RAM, Words 0000-7FFF | |
| assign io_out[1] = spi_cs1; // SPI bus, Chip Select for ROM, Words 8000-FFFF | |
| assign io_out[2] = spi_clk; // SPI bus, Clock | |
| assign io_out[3] = spi_mosi; // SPI bus, ASIC output, target input | |
| assign io_out[4] = uart_tx; // Serial port, ASIC Transmit | |
| assign io_out[5] = out5; | |
| assign io_out[6] = out6; | |
| assign io_out[7] = out7; | |
| // --- Internal Timing Signals --- | |
| wire ShiftAddr; | |
| wire ShiftDataRead; | |
| wire ShiftDataWrite; | |
| wire PresetCarry; | |
| wire EndOfPhase; | |
| wire PrepOutput; | |
| // --- SPI Control Signals | |
| wire Addr15; | |
| wire Read_notWrite; | |
| wire SPIAddr; | |
| wire SPIDataIn; | |
| // --- CPU Registers --- | |
| reg [15:0] PC; | |
| reg [15:0] TMP; | |
| reg [15:0] ADR; | |
| reg PCCarry; | |
| reg TBorrow; | |
| reg TZero; | |
| reg LEQ; | |
| assign out7 = !in7; // For bring-up testing, out7 = !in7. No other internal connections | |
| SPIController spi ( | |
| // System Interfaces | |
| .clk(clk), | |
| .rst(rst), | |
| // SPI Bus Interfaces | |
| .CS0(spi_cs0), | |
| .CS1(spi_cs1), | |
| .SPICLK(spi_clk), | |
| .MOSI(spi_mosi), | |
| .MISO(spi_miso), | |
| // Input Signals | |
| .Addr15(Addr15), | |
| .Read_notWrite(Read_notWrite), | |
| .Addr(SPIAddr), | |
| .Data(SPIDataIn), | |
| // Timing Output Signals | |
| .ShiftAddr(ShiftAddr), | |
| .ShiftDataRead(ShiftDataRead), | |
| .ShiftDataWrite(ShiftDataWrite), | |
| .PresetCarry(PresetCarry), | |
| .EndOfPhase(EndOfPhase), | |
| .PrepOutput(PrepOutput) | |
| ); | |
| reg [2:0] CPUphase; | |
| always @(posedge clk) begin | |
| if (rst) | |
| CPUphase <= 3'd0; | |
| else if (!EndOfPhase) | |
| CPUphase <= CPUphase; | |
| else begin | |
| if (CPUphase == 3'd5) | |
| CPUphase <= 3'd0; | |
| else | |
| CPUphase <= CPUphase + 3'd1; | |
| end | |
| end | |
| wire PCphase = (CPUphase == 0) || (CPUphase == 2) || (CPUphase == 5); | |
| assign Addr15 = PCphase ? PC[15] : ADR[15]; | |
| assign Read_notWrite = (CPUphase != 4); | |
| always @(posedge clk) begin | |
| if (rst) | |
| PC <= 16'h8000; | |
| else begin | |
| if (PresetCarry) | |
| PCCarry <= 1; | |
| if (PCphase && ShiftAddr) begin | |
| PCCarry <= PC[0] & PCCarry; | |
| PC <= {PC[0] ^ PCCarry, PC[15:1]}; | |
| end | |
| if ((CPUphase == 5) && ShiftDataRead) begin | |
| PC <= {LEQ ? spi_miso : PC[0], PC[15:1]}; | |
| end | |
| end | |
| end | |
| assign SPIAddr = PCphase ? PC[0] : ADR[0]; | |
| assign SPIDataIn = TMP[0]; | |
| wire ReadADR = (CPUphase == 0) || (CPUphase == 2); | |
| wire ReadTMP = (CPUphase == 1) || (CPUphase == 3); | |
| always @(posedge clk) begin | |
| if (ReadADR & ShiftDataRead) | |
| ADR <= {spi_miso, ADR[15:1]}; | |
| if (!PCphase & ShiftAddr) | |
| ADR <= {ADR[0], ADR[15:1]}; | |
| end | |
| // Transmit UART | |
| reg BwasFFFF; | |
| reg UARTout; | |
| assign uart_tx = UARTout; | |
| reg [4:0] UARTcount; | |
| always @(posedge clk) begin | |
| if (EndOfPhase) begin | |
| BwasFFFF <= 1; | |
| UARTout <= 1; | |
| UARTcount <= 0; | |
| end | |
| if (ShiftAddr & !ADR[0]) | |
| BwasFFFF <= 0; | |
| if (BwasFFFF & (CPUphase == 3)& PrepOutput) begin | |
| UARTout <= 0; | |
| UARTcount <= 9; | |
| end | |
| if ((UARTcount != 0) & ShiftDataRead) begin | |
| UARTcount <= UARTcount - 1; | |
| UARTout <= (UARTcount != 1) ? TMP[0] : 1; | |
| end; | |
| end | |
| wire sub_b; | |
| wire sub_r; | |
| assign {sub_b, sub_r} = spi_miso - TMP[0] - TBorrow; | |
| always @(posedge clk) begin | |
| if (PresetCarry) begin | |
| TBorrow <= 0; | |
| TZero <= 1; | |
| end | |
| if ((CPUphase == 1) & ShiftDataRead) | |
| TMP <= {spi_miso, TMP[15:1]}; | |
| if ((CPUphase == 3) & ShiftDataRead) begin | |
| TBorrow <= sub_b; | |
| TMP <= {sub_r, TMP[15:1]}; | |
| if (sub_r) | |
| TZero <= 0; | |
| end | |
| if (!Read_notWrite & ShiftDataWrite) | |
| TMP <= {TMP[0], TMP[15:1]}; | |
| end | |
| always @(posedge clk) begin | |
| if (EndOfPhase & (CPUphase == 3)) begin | |
| LEQ <= TZero | TMP[15]; | |
| end | |
| end | |
| endmodule |