1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00

Fixing spi controller

- Manually merging work from @olajep in PR #85 (too far out of sync)
- Fixing issue with lsbfirst logic
- Adding logic for manual control of slave select
- Fixing status register for polling reads
- Still a timing/glitch issue on mosi, fix it later...
This commit is contained in:
Andreas Olofsson 2016-08-24 00:20:51 -04:00
parent f4a74b462f
commit f7ce7b800c
6 changed files with 116 additions and 74 deletions

View File

@ -34,6 +34,11 @@ module spi #( parameter AW = 32, // address width
output s_miso // slave output
);
//1. set manual_CS mode
//2. assert ss (0000)
//4. write to tx fifo
//6. deassert ss (1111)
//###############
//# LOCAL WIRES
//###############

View File

@ -47,10 +47,12 @@ module spi_master # ( parameter DEPTH = 32, // fifo depth
wire fifo_read; // From spi_master_io of spi_master_io.v
wire fifo_wait; // From spi_master_fifo of spi_master_fifo.v
wire lsbfirst; // From spi_master_regs of spi_master_regs.v
wire manual_mode; // From spi_master_regs of spi_master_regs.v
wire rx_access; // From spi_master_io of spi_master_io.v
wire [63:0] rx_data; // From spi_master_io of spi_master_io.v
wire send_data; // From spi_master_regs of spi_master_regs.v
wire spi_en; // From spi_master_regs of spi_master_regs.v
wire [1:0] spi_state; // From spi_master_io of spi_master_io.v
wire [2:0] spi_state; // From spi_master_io of spi_master_io.v
// End of automatics
//#####################################################
@ -65,6 +67,8 @@ module spi_master # ( parameter DEPTH = 32, // fifo depth
.cpha (cpha),
.lsbfirst (lsbfirst),
.spi_en (spi_en),
.manual_mode (manual_mode),
.send_data (send_data),
.clkdiv_reg (clkdiv_reg[7:0]),
.wait_out (wait_out),
.access_out (access_out),
@ -75,7 +79,7 @@ module spi_master # ( parameter DEPTH = 32, // fifo depth
.hw_en (hw_en),
.rx_data (rx_data[63:0]),
.rx_access (rx_access),
.spi_state (spi_state[1:0]),
.spi_state (spi_state[2:0]),
.fifo_prog_full (fifo_prog_full),
.fifo_wait (fifo_wait),
.access_in (access_in),
@ -116,7 +120,7 @@ module spi_master # ( parameter DEPTH = 32, // fifo depth
spi_master_io
spi_master_io (/*AUTOINST*/
// Outputs
.spi_state (spi_state[1:0]),
.spi_state (spi_state[2:0]),
.fifo_read (fifo_read),
.rx_data (rx_data[63:0]),
.rx_access (rx_access),
@ -129,6 +133,8 @@ module spi_master # ( parameter DEPTH = 32, // fifo depth
.cpol (cpol),
.cpha (cpha),
.lsbfirst (lsbfirst),
.manual_mode (manual_mode),
.send_data (send_data),
.clkdiv_reg (clkdiv_reg[7:0]),
.fifo_dout (fifo_dout[7:0]),
.fifo_empty (fifo_empty),

View File

@ -12,8 +12,10 @@ module spi_master_io
input cpol, // cpol
input cpha, // cpha
input lsbfirst, // send lsbfirst
input [7:0] clkdiv_reg, // baudrate
output reg [1:0] spi_state, // current spi tx state
input manual_mode,// sets automatic ss mode
input send_data, // controls ss in manual ss mode
input [7:0] clkdiv_reg, // baudrate
output reg [2:0] spi_state, // current spi tx state
// data to transmit
input [7:0] fifo_dout, // data payload
input fifo_empty, //
@ -32,7 +34,9 @@ module spi_master_io
//# LOCAL WIRES
//###############
reg fifo_empty_reg;
reg load_byte;
reg load_byte;
wire rx_access;
reg ss_reg;
wire [7:0] data_out;
wire [15:0] clkphase0;
wire period_match;
@ -42,7 +46,7 @@ module spi_master_io
wire data_done;
wire spi_wait;
wire shift;
wire spi_active;
/*AUTOWIRE*/
@ -76,24 +80,27 @@ module spi_master_io
//# STATE MACHINE
//#################################
`define SPI_IDLE 2'b00 // set ss to 1
`define SPI_SETUP 2'b01 // setup time
`define SPI_DATA 2'b10 // send data
`define SPI_HOLD 2'b11 // hold time
`define SPI_IDLE 3'b000 // set ss to 1
`define SPI_SETUP 3'b001 // setup time
`define SPI_DATA 3'b010 // send data
`define SPI_HOLD 3'b011 // hold time
`define SPI_MARGIN 3'b100 // pause
always @ (posedge clk or negedge nreset)
if(!nreset)
spi_state[1:0] <= `SPI_IDLE;
spi_state[2:0] <= `SPI_IDLE;
else
case (spi_state[1:0])
case (spi_state[2:0])
`SPI_IDLE :
spi_state[1:0] <= fifo_read ? `SPI_SETUP : `SPI_IDLE;
spi_state[2:0] <= fifo_read ? `SPI_SETUP : `SPI_IDLE;
`SPI_SETUP :
spi_state[1:0] <= phase_match ? `SPI_DATA : `SPI_SETUP;
spi_state[2:0] <= phase_match ? `SPI_DATA : `SPI_SETUP;
`SPI_DATA :
spi_state[1:0] <= data_done ? `SPI_HOLD : `SPI_DATA;
spi_state[2:0] <= data_done ? `SPI_HOLD : `SPI_DATA;
`SPI_HOLD :
spi_state[1:0] <= phase_match ? `SPI_IDLE : `SPI_HOLD;
spi_state[2:0] <= phase_match ? `SPI_MARGIN : `SPI_HOLD;
`SPI_MARGIN :
spi_state[2:0] <= phase_match ? `SPI_IDLE : `SPI_MARGIN;
endcase // case (spi_state[1:0])
//read fifo on phase match (due to one cycle pipeline latency
@ -101,10 +108,7 @@ module spi_master_io
//data done whne
assign data_done = fifo_empty & ~spi_wait & phase_match;
//shift on every clock cycle while in datamode
assign shift = phase_match & (spi_state[1:0]==`SPI_DATA);//period_match
//load is the result of the fifo_read
always @ (posedge clk)
load_byte <= fifo_read;
@ -112,19 +116,24 @@ module spi_master_io
//#################################
//# CHIP SELECT
//#################################
assign ss = (spi_state[1:0]==`SPI_IDLE);
assign spi_active = ~(spi_state[2:0]==`SPI_IDLE | spi_state[2:0]==`SPI_MARGIN);
assign ss = ~((spi_active & ~manual_mode) | (send_data & manual_mode));
//#################################
//# DRIVE OUTPUT CLOCK
//#################################
assign sclk = clkout & (spi_state[1:0]==`SPI_DATA);
assign sclk = clkout & (spi_state[2:0]==`SPI_DATA);
//#################################
//# TX SHIFT REGISTER
//#################################
//shift on falling edge
assign tx_shift = phase_match & (spi_state[2:0]==`SPI_DATA);
oh_par2ser #(.PW(8),
.SW(1))
par2ser (// Outputs
@ -135,7 +144,7 @@ module spi_master_io
.clk (clk),
.nreset (nreset), // async active low reset
.din (fifo_dout[7:0]), // 8 bit data from fifo
.shift (shift), // shift on neg edge
.shift (tx_shift), // shift on neg edge
.datasize (8'd7), // 8 bits at a time (0..7-->8)
.load (load_byte), // load data from fifo
.lsbfirst (lsbfirst), // serializer direction
@ -146,13 +155,9 @@ module spi_master_io
//# RX SHIFT REGISTER
//#################################
//generate access pulse at rise of ss
oh_rise2pulse
pulse (.out (rx_access),
.nreset (nreset),
.clk (clk),
.in (ss));
//shift in rising edge
assign rx_shift = (spi_state[2:0] == `SPI_DATA) & period_match;
oh_ser2par #(.PW(64),
.SW(1))
ser2par (//output
@ -161,7 +166,17 @@ module spi_master_io
.din (miso), // serial data in
.clk (clk), // shift clk
.lsbfirst (lsbfirst), // shift direction
.shift (shift)); // shift data
.shift (rx_shift)); // shift data
//generate access pulse at rise of ss
always @ (posedge clk or negedge nreset)
if(!nreset)
ss_reg <= 1'b1;
else
ss_reg <= ss;
assign rx_access = ss & ~ss_reg;
endmodule // spi_master_io
// Local Variables:

View File

@ -6,6 +6,7 @@
//#############################################################################
`include "spi_regmap.vh"
module spi_master_regs # (parameter CLKDIV = 1, // default clkdiv
parameter AW = 32, // addresss width
parameter PW = 104 // packet width
@ -14,7 +15,7 @@ module spi_master_regs # (parameter CLKDIV = 1, // default clkdiv
//clk,reset, cfg
input clk, // core clock
input nreset, // async active low reset
input hw_en, // block enable pin
input hw_en, // block enable
//io interface
input [63:0] rx_data, // rx data
input rx_access, // rx access pulse
@ -23,8 +24,10 @@ module spi_master_regs # (parameter CLKDIV = 1, // default clkdiv
output cpha, // clk phase shift (default is 0)
output lsbfirst, // send lsbfirst
output spi_en, // enable transmitter
output manual_mode,// sets manual ss control
output send_data, // controls ss in manual ss mode
output reg [7:0] clkdiv_reg, // baud rate setting
input [1:0] spi_state, // transmit state
input [2:0] spi_state, // transmit state
input fifo_prog_full, // fifo reached half/full
input fifo_wait, // tx transfer wait
//packet to transmit
@ -113,7 +116,9 @@ module spi_master_regs # (parameter CLKDIV = 1, // default clkdiv
assign cpol = config_reg[2]; // cpol
assign cpha = config_reg[3]; // cpha
assign lsbfirst = config_reg[4]; // send lsb first
assign manual_mode = config_reg[5]; // manual control of ss bit
assign send_data = config_reg[6]; // ss bit
//####################################
//# STATUS
//####################################
@ -124,10 +129,10 @@ module spi_master_regs # (parameter CLKDIV = 1, // default clkdiv
else if(status_write)
status_reg[7:0] <= reg_wdata[7:0];
else
status_reg[7:0] <= {5'b0, //7:3
fifo_prog_full, //2
|spi_state[1:0], //1
(rx_access | status_reg[0])};//0
status_reg[7:0] <= {5'b0, //7:4
fifo_prog_full, //3
1'b0, //reserved
(rx_access | (~tx_write & status_reg[0]))};//0
//####################################
//# CLKDIV

View File

@ -1,5 +1,6 @@
//#############################################################################
//# Purpose: SPI slave IO state-machine #
//# NOTE: only cpol=0, cpha=0 supported for now!! #
//#############################################################################
//# Author: Andreas Olofsson #
//# License: MIT (see LICENSE file in OH! repository) #
@ -44,14 +45,23 @@ module spi_slave_io #( parameter PW = 104 // packet width
wire [63:0] tx_data;
wire rx_shift;
wire tx_load;
wire tx_shift;
wire tx_wait;
wire ss_sync;
wire ss_pulse;
wire spi_fetch;
wire byte_done;
//#################################
//# MODES: TODO!
//#################################
//cpol=0,cpha=0
//(launch on negedge, capture on posedge)
assign shift = ~ss & spi_en;
assign rx_clk = sclk;
assign tx_clk = ~sclk;
assign tx_load = next_byte;
//#################################
//# STATE MACHINE
//#################################
@ -67,7 +77,7 @@ module spi_slave_io #( parameter PW = 104 // packet width
else
case (spi_state[1:0])
`SPI_IDLE_STATE : spi_state[1:0] <= `SPI_CMD_STATE;
`SPI_CMD_STATE : spi_state[1:0] <= byte_done ? `SPI_DATA_STATE : `SPI_CMD_STATE;
`SPI_CMD_STATE : spi_state[1:0] <= next_byte ? `SPI_DATA_STATE : `SPI_CMD_STATE;
`SPI_DATA_STATE : spi_state[1:0] <= `SPI_DATA_STATE;
endcase // case (spi_state[1:0])
@ -77,17 +87,20 @@ module spi_slave_io #( parameter PW = 104 // packet width
bit_count[7:0] <= 'b0;
else
bit_count[7:0] <= bit_count[7:0] + 1'b1;
assign next_byte = (spi_state[1:0]!=`SPI_IDLE_STATE) &
(bit_count[2:0]==3'b000);
assign byte_done = (spi_state[1:0]!=`SPI_IDLE_STATE) &
(bit_count[2:0]==3'b000);
(bit_count[2:0]==3'b111);
// command/address register
// auto increment for every byte
always @ (negedge sclk or negedge nreset)
always @ (posedge sclk or negedge nreset)
if(!nreset)
command_reg[7:0] <= 'b0;
else if((spi_state[1:0]==`SPI_CMD_STATE) & byte_done)
command_reg[7:0] <= rx_data[7:0];
command_reg[7:0] <= spi_wdata[7:0];
else if(byte_done)
command_reg[7:0] <= {command_reg[7:6],
command_reg[5:0] + 1'b1};
@ -96,17 +109,17 @@ module spi_slave_io #( parameter PW = 104 // packet width
//# SPI RX SHIFT REGISTER
//#################################
assign rx_shift = ~ss & spi_en;
assign shift = ~ss & spi_en;
oh_ser2par #(.PW(8),
.SW(1))
rx_ser2par (// Outputs
.dout (rx_data[7:0]),
// Inputs
.clk (sclk),
.clk (rx_clk),
.din (mosi),
.lsbfirst (lsbfirst), //msb first
.shift (rx_shift));
.shift (shift));
//####################################
//# REMOTE TRANSAXTION SHIFT REGISTER
@ -117,27 +130,24 @@ module spi_slave_io #( parameter PW = 104 // packet width
e_ser2par (// Outputs
.dout (packet_out[PW-1:0]),
// Inputs
.clk (sclk),
.clk (rx_clk),
.din (mosi),
.lsbfirst (lsbfirst), //msb first
.shift (rx_shift));//rx_shift
.shift (shift));//rx_shift
//#################################
//# TX SHIFT REGISTER
//#################################
assign tx_load = byte_done; // & (spi_state[1:0]==`SPI_CMD_STATE);
assign tx_shift = ~ss & spi_en;
oh_par2ser #(.PW(8),
.SW(1))
tx_par2ser (.dout (miso),
.access_out (),
.wait_out (tx_wait),
.clk (sclk), // shift out on positive edge
.clk (tx_clk), // shift out on positive edge
.nreset (~ss),
.din (spi_rdata[7:0]),
.shift (tx_shift),
.shift (shift),
.lsbfirst (lsbfirst),
.load (tx_load),
.datasize (8'd7),
@ -148,7 +158,7 @@ module spi_slave_io #( parameter PW = 104 // packet width
//# REGISTER FILE INTERFACE
//#################################
assign spi_clk = sclk;
assign spi_clk = rx_clk;
assign spi_addr[5:0] = command_reg[5:0];
@ -158,24 +168,24 @@ module spi_slave_io #( parameter PW = 104 // packet width
(command_reg[7:6]==`SPI_WR) &
(spi_state[1:0]==`SPI_DATA_STATE);
assign spi_wdata[7:0] = rx_data[7:0];
assign spi_wdata[7:0] = lsbfirst ? {mosi, rx_data[7:1]}
: {rx_data[6:0], mosi};
//###################################
//# REMOTE FETCH LOGIC
//###################################
//sync the ss to free running clk
//look for rising edge
oh_dsync dsync (.dout (ss_sync),
.clk (clk),
.nreset(nreset),
.din (ss));
//create single cycle pulse
oh_rise2pulse r2p (.out (ss_pulse),
.clk (clk),
.in (ss_sync));
oh_rise2pulse r2p (.nreset (nreset),
.out (ss_pulse),
.clk (clk),
.in (ss_sync));
assign spi_fetch = ss_pulse & (command_reg[7:6]==`SPI_FETCH);
@ -185,7 +195,7 @@ module spi_slave_io #( parameter PW = 104 // packet width
access_out <= 1'b0;
else if(~wait_in)
access_out <= spi_fetch;
endmodule // spi_slave_io
// Local Variables:
// verilog-library-directories:("." "../../common/hdl")

View File

@ -108,6 +108,14 @@ module spi_slave_regs #( parameter UREGS = 13, // # of user regs (max 48)
assign lsbfirst = spi_config[4]; // lsb shifted in first
assign valid = spi_config[5]; // user regs enable
//#####################################
//# USER SPACE REGISTERS
//#####################################
always @ (negedge spi_clk)
if(user_write)
user_regs[spi_addr[4:0]] <= spi_wdata[7:0];
//#####################################
//# STATUS [1]
//#####################################
@ -117,7 +125,7 @@ module spi_slave_regs #( parameter UREGS = 13, // # of user regs (max 48)
spi_status[7:0] <= 8'b0; // clears previous data ready
else if(access_in)
spi_status[7:0] <= 8'd1; // data ready
//#####################################
//# RX DATA FOR FETCH
//#####################################
@ -126,15 +134,8 @@ module spi_slave_regs #( parameter UREGS = 13, // # of user regs (max 48)
always @ (posedge clk)
if(access_in)
core_regs[63:0] <= core_data[63:0];
//#####################################
//# USER SPACE REGISTERS
//#####################################
always @ (negedge spi_clk)
if(user_write)
user_regs[spi_addr[4:0]] <= spi_wdata[7:0];
//#####################################
//# REGISTER VECTOR (FOR FLEXIBILITY)
//#####################################