/////////////////////////////////////////////////////////////////////////// | |
// M0 - 16-bit serial SUBLEQ processor | |
// | |
// Copyright 2022 William Moyes | |
// | |
`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 // | |
); | |
// 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; // TODO: Generate the Address Shift timing pulse output | |
end else if (SPIphase <= 49) | |
MOSI <= 0; | |
else begin | |
if (Read_notWrite) | |
MOSI <= 0; | |
else begin | |
if (SPIphase[0] == 0) | |
MOSI <= Data; // TODO: Generate the Address Shift timing pulse output | |
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); | |
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 ROM, Words 0000-7FFF | |
assign io_out[1] = spi_cs1; // SPI bus, Chip Select for RAM, 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; | |
// --- 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; | |
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) | |
); | |
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'h0000; | |
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 | |
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 | TBorrow; | |
end | |
end | |
endmodule |