blob: 69084a0070beb8d1b8d03b28d6334968cc43bea0 [file] [log] [blame]
module SPIDevice #(
parameter ID = 4'h0,
parameter CLOCK_WIDTH = 8
)(
input wire clk,
input wire rst,
// Peripheral bus
input wire peripheralEnable,
input wire peripheralBus_we,
input wire peripheralBus_oe,
output wire peripheralBus_busy,
input wire[15:0] peripheralBus_address,
input wire[3:0] peripheralBus_byteSelect,
output reg[31:0] peripheralBus_dataRead,
input wire[31:0] peripheralBus_dataWrite,
output wire requestOutput,
// SPI interface
output wire spi_en,
output reg spi_clk,
output wire spi_mosi,
input wire spi_miso,
output wire spi_cs
);
localparam CLOCK_BITS = $clog2(CLOCK_WIDTH);
localparam STATE_IDLE = 2'b00;
localparam STATE_SETUP = 2'b01;
localparam STATE_SHIFT = 2'b10;
localparam STATE_END = 2'b11;
// Device select
wire[11:0] localAddress;
wire deviceEnable;
DeviceSelect #(.ID(ID)) select(
.peripheralEnable(peripheralEnable),
.peripheralBus_address(peripheralBus_address),
.localAddress(localAddress),
.deviceEnable(deviceEnable));
// Register
// Configuration register Default 0x064
// b00-b02: clockScale Default 0x4
// b03-04: spiMode Default 0x0
// b05: msbFirst Default 0x1
// b06: useCS Default 0x1
// b07: activeHighCS Default 0x0
// b08: enable Default 0x0
wire[31:0] configurationRegisterOutputData;
wire configurationRegisterOutputRequest;
wire[8:0] configuration;
ConfigurationRegister #(.WIDTH(9), .ADDRESS(12'h000), .DEFAULT(9'h064)) configurationRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(configurationRegisterOutputData),
.requestOutput(configurationRegisterOutputRequest),
.currentValue(configuration));
wire[2:0] clockScale = configuration[2:0];
wire[1:0] spiMode = configuration[4:3];
wire msbFirst = configuration[5];
wire useCS = configuration[6];
wire activeHighCS = configuration[7];
assign spi_en = configuration[8];
wire spiClockPolarity = spiMode[1];
wire spiSampleMode = spiMode[0];
// Status register
wire deviceBusy = state != STATE_IDLE;
wire[31:0] statusRegisterOutputData;
wire statusRegisterOutputRequest;
wire statusRegisterWriteData_nc;
wire statusRegisterWriteDataEnable_nc;
wire statusRegisterReadDataEnable_nc;
wire statusRegisterBusBusy_nc;
DataRegister #(.WIDTH(1), .ADDRESS(12'h004)) statusRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_busy(statusRegisterBusBusy_nc),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(statusRegisterOutputData),
.requestOutput(statusRegisterOutputRequest),
.writeData(statusRegisterWriteData_nc),
.writeData_en(statusRegisterWriteDataEnable_nc),
.writeData_busy(1'b0),
.readData(deviceBusy),
.readData_en(statusRegisterReadDataEnable_nc),
.readData_busy(1'b0));
// Input and Output register
wire[31:0] dataRegisterOutputData;
wire dataRegisterOutputRequest;
wire[7:0] dataRegisterReadData;
wire[7:0] dataRegisterWriteData;
wire dataRegisterWriteDataEnable;
wire dataRegisterBusBusy;
wire dataRegisterReadDataEnable_nc;
DataRegister #(.WIDTH(8), .ADDRESS(12'h008)) dataRegister(
.clk(clk),
.rst(rst),
.enable(deviceEnable),
.peripheralBus_we(peripheralBus_we),
.peripheralBus_oe(peripheralBus_oe),
.peripheralBus_busy(dataRegisterBusBusy),
.peripheralBus_address(localAddress),
.peripheralBus_byteSelect(peripheralBus_byteSelect),
.peripheralBus_dataWrite(peripheralBus_dataWrite),
.peripheralBus_dataRead(dataRegisterOutputData),
.requestOutput(dataRegisterOutputRequest),
.writeData(dataRegisterWriteData),
.writeData_en(dataRegisterWriteDataEnable),
.writeData_busy(deviceBusy),
.readData(dataRegisterReadData),
.readData_en(dataRegisterReadDataEnable_nc),
.readData_busy(deviceBusy));
// State control
reg[1:0] state = STATE_IDLE;
reg[2:0] bitCounter = 3'b0;
wire[2:0] nextBitCounter = bitCounter + 1;
reg[CLOCK_WIDTH-1:0] clockCounter = {CLOCK_WIDTH{1'b0}};
wire[CLOCK_WIDTH-1:0] nextClockCounter = clockCounter + 1;
wire[CLOCK_WIDTH-1:0] clockScaleCompare = { {(CLOCK_WIDTH-1){1'b0}}, 1'b1 } << clockScale;
wire[CLOCK_WIDTH-1:0] clockScaleHalfCompare = { 1'b0, clockScaleCompare[CLOCK_WIDTH-1:1] };
wire halfClockCounterMatch = nextClockCounter == clockScaleHalfCompare;
wire clockCounterMatch = nextClockCounter == clockScaleCompare;
reg spiClockRise = 1'b0;
reg spiClockFall = 1'b0;
reg spiClock;
wire shiftInEnable = spiSampleMode ? spiClockFall : spiClockRise;
wire shiftOutEnable = spiSampleMode ? spiClockRise : spiClockFall;
wire serialOut;
ShiftRegister #(.WIDTH(8)) register (
.clk(clk),
.rst(rst),
.loadEnable(dataRegisterWriteDataEnable),
.shiftInEnable(shiftInEnable),
.shiftOutEnable(shiftOutEnable),
.msbFirst(msbFirst),
.parallelIn(dataRegisterWriteData),
.parallelOut(dataRegisterReadData),
.serialIn(spi_miso),
.serialOut(serialOut));
always @(posedge clk) begin
if (rst) begin
state <= STATE_IDLE;
bitCounter <= 3'b0;
clockCounter <= {CLOCK_WIDTH{1'b0}};
spiClockRise <= 1'b0;
spiClockFall <= 1'b0;
spiClock <= 1'b0;
end else begin
case (state)
STATE_IDLE: begin
bitCounter <= 3'b0;
clockCounter <= {CLOCK_WIDTH{1'b0}};
spiClockRise <= 1'b0;
spiClockFall <= 1'b0;
spiClock <= 1'b0;
if (dataRegisterWriteDataEnable && peripheralBus_byteSelect[0]) begin
state <= STATE_SETUP;
end
end
STATE_SETUP: begin
clockCounter <= nextClockCounter;
if (halfClockCounterMatch) begin
bitCounter <= 1'b0;
spiClock <= 1'b1;
state <= STATE_SHIFT;
if (spiClockPolarity) begin
spiClockRise <= 1'b0;
spiClockFall <= 1'b1;
end else begin
spiClockRise <= 1'b1;
spiClockFall <= 1'b0;
end
end
end
STATE_SHIFT: begin
if (clockCounterMatch) begin
clockCounter <= {CLOCK_WIDTH{1'b0}};
spiClock <= 1'b0;
if (spiClockPolarity) begin
spiClockRise <= 1'b1;
spiClockFall <= 1'b0;
end else begin
spiClockRise <= 1'b0;
spiClockFall <= 1'b1;
end
if (bitCounter == 3'h7) begin
state <= STATE_END;
end else begin
bitCounter <= nextBitCounter;
end
end else if (halfClockCounterMatch) begin
if (spiClockPolarity) begin
spiClockRise <= 1'b0;
spiClockFall <= 1'b1;
end else begin
spiClockRise <= 1'b1;
spiClockFall <= 1'b0;
end
spiClock <= 1'b1;
clockCounter <= nextClockCounter;
end else begin
spiClockRise <= 1'b0;
spiClockFall <= 1'b0;
clockCounter <= nextClockCounter;
end
end
STATE_END: begin
spiClockRise <= 1'b0;
spiClockFall <= 1'b0;
spiClock <= 1'b0;
if (clockCounterMatch) state <= STATE_IDLE;
else clockCounter <= nextClockCounter;
end
default: begin
state <= STATE_IDLE;
bitCounter <= 3'b0;
clockCounter <= {CLOCK_WIDTH{1'b0}};
spiClockRise <= 1'b0;
spiClockFall <= 1'b0;
spiClock <= 1'b0;
end
endcase
end
end
// Buffer the spi clock by one cycle so that it lines up with when data is sampled
always @(posedge clk) begin
if (rst) spi_clk <= 1'b0;
else spi_clk <= spiClockPolarity ? !spiClock : spiClock;
end
assign requestOutput = configurationRegisterOutputRequest || statusRegisterOutputRequest || dataRegisterOutputRequest;
always @(*) begin
case (1'b1)
configurationRegisterOutputRequest: peripheralBus_dataRead <= configurationRegisterOutputData;
statusRegisterOutputRequest: peripheralBus_dataRead <= statusRegisterOutputData;
dataRegisterOutputRequest: peripheralBus_dataRead <= dataRegisterOutputData;
default: peripheralBus_dataRead <= ~32'b0;
endcase
end
assign peripheralBus_busy = dataRegisterBusBusy;
assign spi_mosi = serialOut & deviceBusy;
assign spi_cs = useCS ? (activeHighCS ? deviceBusy : !deviceBusy) : 1'b0;
endmodule