blob: e87a90ad4cd7d8e1ffe4a90b08d42049b82120a3 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////
// 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