Dateien hochladen nach „FRAM Controller“
This commit is contained in:
parent
a28e3a9f5e
commit
6e745d4367
199
FRAM Controller/FRAM_Controller.sv
Normal file
199
FRAM Controller/FRAM_Controller.sv
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
`include "SPI.sv"
|
||||||
|
|
||||||
|
module FRAM(
|
||||||
|
input i_clk, //Module (Module CLock = SPI Clock)
|
||||||
|
input i_nreset,
|
||||||
|
|
||||||
|
input logic [19:0] i_adr, //Memorycell adress in FRAM
|
||||||
|
input logic [7:0] i_data, //data to write
|
||||||
|
output logic [7:0] o_data, //data to read
|
||||||
|
|
||||||
|
input logic i_rw, //Read = 1, Write = 0
|
||||||
|
input logic i_status, //If 1 Read Staut register
|
||||||
|
input logic i_hbn, //If 1 FRAM will enter Hibernation Mode
|
||||||
|
input logic i_cready, //Starts transmission
|
||||||
|
output logic o_busy, //Indicates FRAM Busy
|
||||||
|
|
||||||
|
// SPI Interface
|
||||||
|
output o_SPI_Clk,
|
||||||
|
input i_SPI_MISO,
|
||||||
|
output o_SPI_MOSI,
|
||||||
|
output o_SPI_CS_n
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//FRAM SPI OP Codes
|
||||||
|
|
||||||
|
//Write Enable Control
|
||||||
|
localparam WREN = 8'h06; //Set Write enable latch
|
||||||
|
localparam WRDI = 8'h04; //Reset write enable latch
|
||||||
|
//Register Access
|
||||||
|
localparam RDSR = 8'h05; //Read Status Register
|
||||||
|
localparam WRSR = 8'h01; //Write Status Register
|
||||||
|
//Memory Write
|
||||||
|
localparam WRITE = 8'h02; //Write Memory Data
|
||||||
|
//Memory Read
|
||||||
|
localparam READ = 8'h03; //Read Memory Data
|
||||||
|
localparam FSTRT = 8'h0B; //Fast read memory Data
|
||||||
|
//Special Sector Memory Access
|
||||||
|
localparam SSWR = 8'h42; //Spcial Sector Write
|
||||||
|
localparam SSRD = 8'h4B; //Special Sector Read
|
||||||
|
//Identification and serial Number
|
||||||
|
localparam RDID = 8'h9F; //Read Device ID
|
||||||
|
localparam RUID = 8'h4C; //Read Unique ID
|
||||||
|
localparam WRSN = 8'hC2; //Write Serial Number
|
||||||
|
localparam RDSN = 8'hC3; //Read Serial Number
|
||||||
|
//Low Power Modes
|
||||||
|
localparam DPD = 8'hBA; // Enter Deep Power-Down
|
||||||
|
localparam HBN = 8'hB9; // Enter Hibernate Mode
|
||||||
|
//end FRAM SPI OP Codes
|
||||||
|
|
||||||
|
//Controller Specific
|
||||||
|
logic [3:0] state;
|
||||||
|
|
||||||
|
|
||||||
|
// SPI Specific
|
||||||
|
parameter SPI_MODE = 0; // CPOL = 0, CPHA = 0
|
||||||
|
parameter CLKS_PER_HALF_BIT = 2; // 25MHz
|
||||||
|
parameter MAX_BYTES_PER_CS = 5; // 5 bytes max per chip select cycle
|
||||||
|
parameter CS_INACTIVE_CLKS = 1; // Adds delay (1clk) between cycles
|
||||||
|
|
||||||
|
|
||||||
|
logic [7:0] r_Master_TX_Byte = 0;
|
||||||
|
logic r_Master_TX_DV = 1'b0;
|
||||||
|
logic w_Master_TX_Ready;
|
||||||
|
logic w_Master_RX_DV;
|
||||||
|
logic [7:0] w_Master_RX_Byte;
|
||||||
|
logic [$clog2(MAX_BYTES_PER_CS+1)-1:0] w_Master_RX_Count, r_Master_TX_Count = 3'h1; //Standard 1 Byte pro CS Cycle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SPI_Master_With_Single_CS
|
||||||
|
#(.SPI_MODE(SPI_MODE), //SPI Mode 0-3
|
||||||
|
.CLKS_PER_HALF_BIT(CLKS_PER_HALF_BIT), //sets Frequency of SPI_CLK
|
||||||
|
.MAX_BYTES_PER_CS(MAX_BYTES_PER_CS), //Maximum Bytes per CS Cycle
|
||||||
|
.CS_INACTIVE_CLKS(CS_INACTIVE_CLKS) //Amount of Time holding CS Low befor next command
|
||||||
|
) SPI
|
||||||
|
(
|
||||||
|
// Control/Data Signals,
|
||||||
|
.i_Rst_L(i_nreset), // FPGA Reset
|
||||||
|
.i_Clk(i_clk), // FPGA Clock
|
||||||
|
|
||||||
|
// TX (MOSI) Signals
|
||||||
|
.i_TX_Count(r_Master_TX_Count), // Number of bytes per CS
|
||||||
|
.i_TX_Byte(r_Master_TX_Byte), // Byte to transmit on MOSI
|
||||||
|
.i_TX_DV(r_Master_TX_DV), // Data Valid Pulse with i_TX_Byte
|
||||||
|
.o_TX_Ready(w_Master_TX_Ready), // Transmit Ready for Byte
|
||||||
|
|
||||||
|
// RX (MISO) Signals
|
||||||
|
.o_RX_Count(w_Master_RX_Count), // Index of RX'd byte
|
||||||
|
.o_RX_DV(w_Master_RX_DV), // Data Valid pulse (1 clock cycle)
|
||||||
|
.o_RX_Byte(w_Master_RX_Byte), // Byte received on MISO
|
||||||
|
|
||||||
|
// SPI Interface
|
||||||
|
.o_SPI_Clk(o_SPI_Clk),
|
||||||
|
.i_SPI_MISO(i_SPI_MISO),
|
||||||
|
.o_SPI_MOSI(o_SPI_MOSI),
|
||||||
|
.o_SPI_CS_n(o_SPI_CS_n)
|
||||||
|
);
|
||||||
|
|
||||||
|
//end SPI Specific
|
||||||
|
|
||||||
|
|
||||||
|
task SPI_SendByte(input [7:0] data);
|
||||||
|
@(posedge i_clk);
|
||||||
|
r_Master_TX_Byte <= data;
|
||||||
|
r_Master_TX_DV <= 1'b1;
|
||||||
|
@(posedge i_clk);
|
||||||
|
r_Master_TX_DV <= 1'b0;
|
||||||
|
@(posedge i_clk);
|
||||||
|
@(posedge w_Master_TX_Ready);
|
||||||
|
endtask //end SPI_SendByte
|
||||||
|
|
||||||
|
//FRAM Tasks
|
||||||
|
task FRAM_Write(input [19:0] adr, input [7:0] data); //vgl. Fig.11
|
||||||
|
|
||||||
|
logic [7:0] value;
|
||||||
|
value <= 8'h0;
|
||||||
|
|
||||||
|
//Set Write Enable
|
||||||
|
r_Master_TX_Count <= 3'b1; //1Byte Transaction
|
||||||
|
SPI_SendByte(WREN);
|
||||||
|
|
||||||
|
//Write to fram
|
||||||
|
r_Master_TX_Count <= 3'h5; //5 Byte Transaction
|
||||||
|
SPI_SendByte(WRITE); //OPCode
|
||||||
|
SPI_SendByte({4'hF,adr[19:16]}); //Adress [23-16]
|
||||||
|
SPI_SendByte(adr[15:8]); //Adress [15-8]
|
||||||
|
SPI_SendByte(adr[7:0]); //Adress [7-0]
|
||||||
|
SPI_SendByte(data); //Data [7:0]
|
||||||
|
|
||||||
|
//Reset Write Disable and Verify
|
||||||
|
do begin
|
||||||
|
r_Master_TX_Count <= 3'b1; //1Byte Transaction
|
||||||
|
SPI_SendByte(WRDI); //Set Write Disable
|
||||||
|
|
||||||
|
FRAM_Read_Status(value); //Lese Status Register
|
||||||
|
end while(((value & 8'h2) >> 1) != 0);
|
||||||
|
|
||||||
|
|
||||||
|
endtask //end FRAM_Write
|
||||||
|
|
||||||
|
task FRAM_Read(input [19:0] adr, output [7:0] data); //vgl. Fig12
|
||||||
|
r_Master_TX_Count <= 3'h5; //5 Byte Transaction
|
||||||
|
SPI_SendByte(READ); //Opcode
|
||||||
|
SPI_SendByte({4'hF,adr[19:16]}); //Adress [23-16]
|
||||||
|
SPI_SendByte(adr[15:8]); //Adress [15-8]
|
||||||
|
SPI_SendByte(adr[7:0]); //Adress [7-0]
|
||||||
|
|
||||||
|
SPI_SendByte(8'hAA); //Dummy Bits, read byte in w_Master_RX_Byte
|
||||||
|
data = w_Master_RX_Byte;
|
||||||
|
|
||||||
|
endtask //end FRAM_READ
|
||||||
|
|
||||||
|
task FRAM_Read_Status(output [7:0] data); //vgl. Fig9
|
||||||
|
r_Master_TX_Count <= 3'h2; //2 Byte Transaction
|
||||||
|
SPI_SendByte(RDSR); //OpCode
|
||||||
|
SPI_SendByte(8'hFD); //Dummy Bits, read byte in w_Master_RX_Byte
|
||||||
|
data = w_Master_RX_Byte;
|
||||||
|
endtask //FRAM_Read_Status
|
||||||
|
|
||||||
|
task FRAM_Hibernation(); //vgl. Fig22
|
||||||
|
r_Master_TX_Count <= 3'h1; //1 Byte Transaction
|
||||||
|
SPI_SendByte(HBN);
|
||||||
|
endtask //FRAM_Hibernation
|
||||||
|
|
||||||
|
|
||||||
|
//end FRAM Tasks
|
||||||
|
|
||||||
|
|
||||||
|
always @(posedge i_clk or negedge i_nreset) begin
|
||||||
|
|
||||||
|
state[0] = i_cready;
|
||||||
|
state[1] = i_hbn;
|
||||||
|
state[2] = i_status;
|
||||||
|
state[3] = i_rw;
|
||||||
|
|
||||||
|
if(~i_nreset) begin //Modul Reset
|
||||||
|
o_data <= 8'h00;
|
||||||
|
end //end if
|
||||||
|
|
||||||
|
if(w_Master_TX_Ready) begin
|
||||||
|
case(state) inside
|
||||||
|
4'b??11: FRAM_Hibernation();
|
||||||
|
4'b?101: FRAM_Read_Status(o_data);
|
||||||
|
4'b1001: FRAM_Read(i_adr, o_data);
|
||||||
|
4'b0001: FRAM_Write(i_adr, i_data);
|
||||||
|
|
||||||
|
default:;
|
||||||
|
endcase //endcase
|
||||||
|
end //endif
|
||||||
|
|
||||||
|
end //end always
|
||||||
|
|
||||||
|
|
||||||
|
assign o_busy = w_Master_TX_Ready;
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
BIN
FRAM Controller/SPI FRAM Controller.pdf
Normal file
BIN
FRAM Controller/SPI FRAM Controller.pdf
Normal file
Binary file not shown.
188
FRAM Controller/SPI_Master.sv
Normal file
188
FRAM Controller/SPI_Master.sv
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//Source: https://github.com/nandland/spi-master/tree/master/Verilog/source
|
||||||
|
//Description: SPI (Serial Peripheral Interface) Master
|
||||||
|
// With single chip-select (AKA Slave Select) capability
|
||||||
|
//
|
||||||
|
// Supports arbitrary length byte transfers.
|
||||||
|
//
|
||||||
|
// Instantiates a SPI Master and adds single CS.
|
||||||
|
// If multiple CS signals are needed, will need to use different
|
||||||
|
// module, OR multiplex the CS from this at a higher level.
|
||||||
|
//
|
||||||
|
// Note: i_Clk must be at least 2x faster than i_SPI_Clk
|
||||||
|
//
|
||||||
|
// Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above.
|
||||||
|
// Can be configured in one of 4 modes:
|
||||||
|
// Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
|
||||||
|
// 0 | 0 | 0
|
||||||
|
// 1 | 0 | 1
|
||||||
|
// 2 | 1 | 0
|
||||||
|
// 3 | 1 | 1
|
||||||
|
//
|
||||||
|
// CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
|
||||||
|
// derived from i_Clk. Set to integer number of clocks for each
|
||||||
|
// half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
|
||||||
|
// would create o_SPI_CLK of 25 MHz. Must be >= 2
|
||||||
|
//
|
||||||
|
// MAX_BYTES_PER_CS - Set to the maximum number of bytes that
|
||||||
|
// will be sent during a single CS-low pulse.
|
||||||
|
//
|
||||||
|
// CS_INACTIVE_CLKS - Sets the amount of time in clock cycles to
|
||||||
|
// hold the state of Chip-Selct high (inactive) before next
|
||||||
|
// command is allowed on the line. Useful if chip requires some
|
||||||
|
// time when CS is high between trasnfers.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
`include "iSPI.sv"
|
||||||
|
|
||||||
|
|
||||||
|
module SPI_Master_With_Single_CS
|
||||||
|
#(parameter SPI_MODE = 0,
|
||||||
|
parameter CLKS_PER_HALF_BIT = 2,
|
||||||
|
parameter MAX_BYTES_PER_CS = 1,
|
||||||
|
parameter CS_INACTIVE_CLKS = 1)
|
||||||
|
(
|
||||||
|
// Control/Data Signals,
|
||||||
|
input i_Rst_L, // FPGA Reset
|
||||||
|
input i_Clk, // FPGA Clock
|
||||||
|
|
||||||
|
// TX (MOSI) Signals
|
||||||
|
input [$clog2(MAX_BYTES_PER_CS+1)-1:0] i_TX_Count, // # bytes per CS low
|
||||||
|
input [7:0] i_TX_Byte, // Byte to transmit on MOSI
|
||||||
|
input i_TX_DV, // Data Valid Pulse with i_TX_Byte
|
||||||
|
output o_TX_Ready, // Transmit Ready for next byte
|
||||||
|
|
||||||
|
// RX (MISO) Signals
|
||||||
|
output reg [$clog2(MAX_BYTES_PER_CS+1)-1:0] o_RX_Count, // Index RX byte
|
||||||
|
output o_RX_DV, // Data Valid pulse (1 clock cycle)
|
||||||
|
output [7:0] o_RX_Byte, // Byte received on MISO
|
||||||
|
|
||||||
|
// SPI Interface
|
||||||
|
output o_SPI_Clk,
|
||||||
|
input i_SPI_MISO,
|
||||||
|
output o_SPI_MOSI,
|
||||||
|
output o_SPI_CS_n
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam IDLE = 2'b00;
|
||||||
|
localparam TRANSFER = 2'b01;
|
||||||
|
localparam CS_INACTIVE = 2'b10;
|
||||||
|
|
||||||
|
reg [1:0] r_SM_CS;
|
||||||
|
reg r_CS_n;
|
||||||
|
reg [$clog2(CS_INACTIVE_CLKS)-1:0] r_CS_Inactive_Count;
|
||||||
|
reg [$clog2(MAX_BYTES_PER_CS+1)-1:0] r_TX_Count;
|
||||||
|
wire w_Master_Ready;
|
||||||
|
|
||||||
|
// Instantiate Master
|
||||||
|
SPI_Master
|
||||||
|
#(.SPI_MODE(SPI_MODE),
|
||||||
|
.CLKS_PER_HALF_BIT(CLKS_PER_HALF_BIT)
|
||||||
|
) SPI_Master_Inst
|
||||||
|
(
|
||||||
|
// Control/Data Signals,
|
||||||
|
.i_Rst_L(i_Rst_L), // FPGA Reset
|
||||||
|
.i_Clk(i_Clk), // FPGA Clock
|
||||||
|
|
||||||
|
// TX (MOSI) Signals
|
||||||
|
.i_TX_Byte(i_TX_Byte), // Byte to transmit
|
||||||
|
.i_TX_DV(i_TX_DV), // Data Valid Pulse
|
||||||
|
.o_TX_Ready(w_Master_Ready), // Transmit Ready for Byte
|
||||||
|
|
||||||
|
// RX (MISO) Signals
|
||||||
|
.o_RX_DV(o_RX_DV), // Data Valid pulse (1 clock cycle)
|
||||||
|
.o_RX_Byte(o_RX_Byte), // Byte received on MISO
|
||||||
|
|
||||||
|
// SPI Interface
|
||||||
|
.o_SPI_Clk(o_SPI_Clk),
|
||||||
|
.i_SPI_MISO(i_SPI_MISO),
|
||||||
|
.o_SPI_MOSI(o_SPI_MOSI)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Control CS line using State Machine
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
r_SM_CS <= IDLE;
|
||||||
|
r_CS_n <= 1'b1; // Resets to high
|
||||||
|
r_TX_Count <= 0;
|
||||||
|
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
|
||||||
|
case (r_SM_CS)
|
||||||
|
IDLE:
|
||||||
|
begin
|
||||||
|
if (r_CS_n & i_TX_DV) // Start of transmission
|
||||||
|
begin
|
||||||
|
r_TX_Count <= i_TX_Count - 1; // Register TX Count
|
||||||
|
r_CS_n <= 1'b0; // Drive CS low
|
||||||
|
r_SM_CS <= TRANSFER; // Transfer bytes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TRANSFER:
|
||||||
|
begin
|
||||||
|
// Wait until SPI is done transferring do next thing
|
||||||
|
if (w_Master_Ready)
|
||||||
|
begin
|
||||||
|
if (r_TX_Count > 0)
|
||||||
|
begin
|
||||||
|
if (i_TX_DV)
|
||||||
|
begin
|
||||||
|
r_TX_Count <= r_TX_Count - 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
r_CS_n <= 1'b1; // we done, so set CS high
|
||||||
|
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
|
||||||
|
r_SM_CS <= CS_INACTIVE;
|
||||||
|
end // else: !if(r_TX_Count > 0)
|
||||||
|
end // if (w_Master_Ready)
|
||||||
|
end // case: TRANSFER
|
||||||
|
|
||||||
|
CS_INACTIVE:
|
||||||
|
begin
|
||||||
|
if (r_CS_Inactive_Count > 0)
|
||||||
|
begin
|
||||||
|
r_CS_Inactive_Count <= r_CS_Inactive_Count - 1'b1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
r_SM_CS <= IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default:
|
||||||
|
begin
|
||||||
|
r_CS_n <= 1'b1; // we done, so set CS high
|
||||||
|
r_SM_CS <= IDLE;
|
||||||
|
end
|
||||||
|
endcase // case (r_SM_CS)
|
||||||
|
end
|
||||||
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Keep track of RX_Count
|
||||||
|
always @(posedge i_Clk)
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
if (r_CS_n)
|
||||||
|
begin
|
||||||
|
o_RX_Count <= 0;
|
||||||
|
end
|
||||||
|
else if (o_RX_DV)
|
||||||
|
begin
|
||||||
|
o_RX_Count <= o_RX_Count + 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign o_SPI_CS_n = r_CS_n;
|
||||||
|
|
||||||
|
assign o_TX_Ready = ((r_SM_CS == IDLE) | (r_SM_CS == TRANSFER && w_Master_Ready == 1'b1 && r_TX_Count > 0)) & ~i_TX_DV;
|
||||||
|
|
||||||
|
endmodule // SPI_Master_With_Single_CS
|
240
FRAM Controller/SPI_Master_Control.sv
Normal file
240
FRAM Controller/SPI_Master_Control.sv
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//Source: https://github.com/nandland/spi-master/tree/master/Verilog/source
|
||||||
|
// Description: SPI (Serial Peripheral Interface) Master
|
||||||
|
// Creates master based on input configuration.
|
||||||
|
// Sends a byte one bit at a time on MOSI
|
||||||
|
// Will also receive byte data one bit at a time on MISO.
|
||||||
|
// Any data on input byte will be shipped out on MOSI.
|
||||||
|
//
|
||||||
|
// To kick-off transaction, user must pulse i_TX_DV.
|
||||||
|
// This module supports multi-byte transmissions by pulsing
|
||||||
|
// i_TX_DV and loading up i_TX_Byte when o_TX_Ready is high.
|
||||||
|
//
|
||||||
|
// This module is only responsible for controlling Clk, MOSI,
|
||||||
|
// and MISO. If the SPI peripheral requires a chip-select,
|
||||||
|
// this must be done at a higher level.
|
||||||
|
//
|
||||||
|
// Note: i_Clk must be at least 2x faster than i_SPI_Clk
|
||||||
|
//
|
||||||
|
// Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above.
|
||||||
|
// Can be configured in one of 4 modes:
|
||||||
|
// Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
|
||||||
|
// 0 | 0 | 0
|
||||||
|
// 1 | 0 | 1
|
||||||
|
// 2 | 1 | 0
|
||||||
|
// 3 | 1 | 1
|
||||||
|
// More: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers
|
||||||
|
// CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
|
||||||
|
// derived from i_Clk. Set to integer number of clocks for each
|
||||||
|
// half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
|
||||||
|
// would create o_SPI_CLK of 25 MHz. Must be >= 2
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module SPI_Master
|
||||||
|
#(parameter SPI_MODE = 0,
|
||||||
|
parameter CLKS_PER_HALF_BIT = 2)
|
||||||
|
(
|
||||||
|
// Control/Data Signals,
|
||||||
|
input i_Rst_L, // FPGA Reset
|
||||||
|
input i_Clk, // FPGA Clock
|
||||||
|
|
||||||
|
// TX (MOSI) Signals
|
||||||
|
input [7:0] i_TX_Byte, // Byte to transmit on MOSI
|
||||||
|
input i_TX_DV, // Data Valid Pulse with i_TX_Byte
|
||||||
|
output reg o_TX_Ready, // Transmit Ready for next byte
|
||||||
|
|
||||||
|
// RX (MISO) Signals
|
||||||
|
output reg o_RX_DV, // Data Valid pulse (1 clock cycle)
|
||||||
|
output reg [7:0] o_RX_Byte, // Byte received on MISO
|
||||||
|
|
||||||
|
// SPI Interface
|
||||||
|
output reg o_SPI_Clk,
|
||||||
|
input i_SPI_MISO,
|
||||||
|
output reg o_SPI_MOSI
|
||||||
|
);
|
||||||
|
|
||||||
|
// SPI Interface (All Runs at SPI Clock Domain)
|
||||||
|
wire w_CPOL; // Clock polarity
|
||||||
|
wire w_CPHA; // Clock phase
|
||||||
|
|
||||||
|
reg [$clog2(CLKS_PER_HALF_BIT*2)-1:0] r_SPI_Clk_Count;
|
||||||
|
reg r_SPI_Clk;
|
||||||
|
reg [4:0] r_SPI_Clk_Edges;
|
||||||
|
reg r_Leading_Edge;
|
||||||
|
reg r_Trailing_Edge;
|
||||||
|
reg r_TX_DV;
|
||||||
|
reg [7:0] r_TX_Byte;
|
||||||
|
|
||||||
|
reg [2:0] r_RX_Bit_Count;
|
||||||
|
reg [2:0] r_TX_Bit_Count;
|
||||||
|
|
||||||
|
// CPOL: Clock Polarity
|
||||||
|
// CPOL=0 means clock idles at 0, leading edge is rising edge.
|
||||||
|
// CPOL=1 means clock idles at 1, leading edge is falling edge.
|
||||||
|
assign w_CPOL = (SPI_MODE == 2) | (SPI_MODE == 3);
|
||||||
|
|
||||||
|
// CPHA: Clock Phase
|
||||||
|
// CPHA=0 means the "out" side changes the data on trailing edge of clock
|
||||||
|
// the "in" side captures data on leading edge of clock
|
||||||
|
// CPHA=1 means the "out" side changes the data on leading edge of clock
|
||||||
|
// the "in" side captures data on the trailing edge of clock
|
||||||
|
assign w_CPHA = (SPI_MODE == 1) | (SPI_MODE == 3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Generate SPI Clock correct number of times when DV pulse comes
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
o_TX_Ready <= 1'b0;
|
||||||
|
r_SPI_Clk_Edges <= 0;
|
||||||
|
r_Leading_Edge <= 1'b0;
|
||||||
|
r_Trailing_Edge <= 1'b0;
|
||||||
|
r_SPI_Clk <= w_CPOL; // assign default state to idle state
|
||||||
|
r_SPI_Clk_Count <= 0;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
|
||||||
|
// Default assignments
|
||||||
|
r_Leading_Edge <= 1'b0;
|
||||||
|
r_Trailing_Edge <= 1'b0;
|
||||||
|
|
||||||
|
if (i_TX_DV)
|
||||||
|
begin
|
||||||
|
o_TX_Ready <= 1'b0;
|
||||||
|
r_SPI_Clk_Edges <= 16; // Total # edges in one byte ALWAYS 16
|
||||||
|
end
|
||||||
|
else if (r_SPI_Clk_Edges > 0)
|
||||||
|
begin
|
||||||
|
o_TX_Ready <= 1'b0;
|
||||||
|
|
||||||
|
if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT*2-1)
|
||||||
|
begin
|
||||||
|
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
|
||||||
|
r_Trailing_Edge <= 1'b1;
|
||||||
|
r_SPI_Clk_Count <= 0;
|
||||||
|
r_SPI_Clk <= ~r_SPI_Clk;
|
||||||
|
end
|
||||||
|
else if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT-1)
|
||||||
|
begin
|
||||||
|
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
|
||||||
|
r_Leading_Edge <= 1'b1;
|
||||||
|
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
|
||||||
|
r_SPI_Clk <= ~r_SPI_Clk;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
o_TX_Ready <= 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end // else: !if(~i_Rst_L)
|
||||||
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Register i_TX_Byte when Data Valid is pulsed.
|
||||||
|
// Keeps local storage of byte in case higher level module changes the data
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
r_TX_Byte <= 8'h00;
|
||||||
|
r_TX_DV <= 1'b0;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
r_TX_DV <= i_TX_DV; // 1 clock cycle delay
|
||||||
|
if (i_TX_DV)
|
||||||
|
begin
|
||||||
|
r_TX_Byte <= i_TX_Byte;
|
||||||
|
end
|
||||||
|
end // else: !if(~i_Rst_L)
|
||||||
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Generate MOSI data
|
||||||
|
// Works with both CPHA=0 and CPHA=1
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
o_SPI_MOSI <= 1'b0;
|
||||||
|
r_TX_Bit_Count <= 3'b111; // send MSb first
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// If ready is high, reset bit counts to default
|
||||||
|
if (o_TX_Ready)
|
||||||
|
begin
|
||||||
|
r_TX_Bit_Count <= 3'b111;
|
||||||
|
end
|
||||||
|
// Catch the case where we start transaction and CPHA = 0
|
||||||
|
else if (r_TX_DV & ~w_CPHA)
|
||||||
|
begin
|
||||||
|
o_SPI_MOSI <= r_TX_Byte[3'b111];
|
||||||
|
r_TX_Bit_Count <= 3'b110;
|
||||||
|
end
|
||||||
|
else if ((r_Leading_Edge & w_CPHA) | (r_Trailing_Edge & ~w_CPHA))
|
||||||
|
begin
|
||||||
|
r_TX_Bit_Count <= r_TX_Bit_Count - 1;
|
||||||
|
o_SPI_MOSI <= r_TX_Byte[r_TX_Bit_Count];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Read in MISO data.
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
o_RX_Byte <= 8'h00;
|
||||||
|
o_RX_DV <= 1'b0;
|
||||||
|
r_RX_Bit_Count <= 3'b111;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
|
||||||
|
// Default Assignments
|
||||||
|
o_RX_DV <= 1'b0;
|
||||||
|
|
||||||
|
if (o_TX_Ready) // Check if ready is high, if so reset bit count to default
|
||||||
|
begin
|
||||||
|
r_RX_Bit_Count <= 3'b111;
|
||||||
|
end
|
||||||
|
else if ((r_Leading_Edge & ~w_CPHA) | (r_Trailing_Edge & w_CPHA))
|
||||||
|
begin
|
||||||
|
o_RX_Byte[r_RX_Bit_Count] <= i_SPI_MISO; // Sample data
|
||||||
|
r_RX_Bit_Count <= r_RX_Bit_Count - 1;
|
||||||
|
if (r_RX_Bit_Count == 3'b000)
|
||||||
|
begin
|
||||||
|
o_RX_DV <= 1'b1; // Byte done, pulse Data Valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
// Purpose: Add clock delay to signals for alignment.
|
||||||
|
always @(posedge i_Clk or negedge i_Rst_L)
|
||||||
|
begin
|
||||||
|
if (~i_Rst_L)
|
||||||
|
begin
|
||||||
|
o_SPI_Clk <= w_CPOL;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
o_SPI_Clk <= r_SPI_Clk;
|
||||||
|
end // else: !if(~i_Rst_L)
|
||||||
|
end // always @ (posedge i_Clk or negedge i_Rst_L)
|
||||||
|
|
||||||
|
|
||||||
|
endmodule // SPI_Master
|
168
FRAM Controller/testbench.sv
Normal file
168
FRAM Controller/testbench.sv
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
module testbench();
|
||||||
|
|
||||||
|
logic clk;
|
||||||
|
logic nReset;
|
||||||
|
|
||||||
|
logic [19:0] FRAM_Adr;
|
||||||
|
logic [7:0] FRAM_DATA_OUT;
|
||||||
|
logic [7:0] FRAM_DATA_IN;
|
||||||
|
logic FRAM_RW;
|
||||||
|
logic FRAM_RSTATUS;
|
||||||
|
logic FRAM_hbn;
|
||||||
|
logic FRAM_go;
|
||||||
|
logic FRAM_busy;
|
||||||
|
|
||||||
|
logic SPI_CLK;
|
||||||
|
logic SPI_MISO;
|
||||||
|
logic SPI_MOSI;
|
||||||
|
logic SPI_CS;
|
||||||
|
|
||||||
|
logic [2:0]test;
|
||||||
|
logic test_running;
|
||||||
|
logic starttesting;
|
||||||
|
|
||||||
|
localparam TESTS_cnt = 5;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Required for EDA Playground
|
||||||
|
$dumpfile("dump.vcd");
|
||||||
|
$dumpvars;
|
||||||
|
|
||||||
|
clk = 1'h0;
|
||||||
|
nReset = 1'h0;
|
||||||
|
|
||||||
|
FRAM_Adr <= 20'h0;
|
||||||
|
FRAM_DATA_IN <= 8'h0;
|
||||||
|
FRAM_RW = 0;
|
||||||
|
FRAM_RSTATUS = 0;
|
||||||
|
FRAM_hbn = 0;
|
||||||
|
FRAM_go = 0;
|
||||||
|
|
||||||
|
test <= 2'h0;
|
||||||
|
|
||||||
|
repeat(10) @(posedge clk);
|
||||||
|
nReset = 1'h1;
|
||||||
|
|
||||||
|
starttesting <= 1'h1;
|
||||||
|
test_running <= 1'h0;
|
||||||
|
|
||||||
|
end //initial end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Clock Generation:
|
||||||
|
always #(5) clk = ~clk; //clk 100MHz
|
||||||
|
// end Clock Generation
|
||||||
|
|
||||||
|
|
||||||
|
always @ (posedge starttesting or posedge FRAM_busy) begin
|
||||||
|
|
||||||
|
repeat(10) @(posedge clk);
|
||||||
|
if(test_running == 1'h0 & FRAM_busy == 1'h1) begin
|
||||||
|
|
||||||
|
if(test == TESTS_cnt+1) begin
|
||||||
|
test_running <= 1'h0;
|
||||||
|
$display("Tests Finished");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
case(test) inside
|
||||||
|
3'b000: begin Test1(); test <= test + 1'h1; end
|
||||||
|
3'b001: begin Test2(); test <= test + 1'h1; end
|
||||||
|
3'b010: begin Test3(); test <= test + 1'h1; end
|
||||||
|
3'b011: begin Test4(); test <= test + 1'h1; end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end // endif
|
||||||
|
end // end always
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
task Test1();
|
||||||
|
test_running <= 1'h1;
|
||||||
|
$display("DEBUG: %0tns: Test_1_Hibernation",$realtime);
|
||||||
|
FRAM_hbn <= 1'h1; //Enter Hibernation
|
||||||
|
FRAM_go <= 1'h1;
|
||||||
|
#10;
|
||||||
|
FRAM_hbn <= 1'h0; //Reset Hibernation Flag
|
||||||
|
FRAM_go <= 1'h0;
|
||||||
|
$display("DEBUG: %0tns: Test_1_Hibernation__-END",$realtime);
|
||||||
|
test_running <= 1'h0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task Test2();
|
||||||
|
test_running <= 1'h1;
|
||||||
|
$display("DEBUG: %0tns: Test_2_ReadStatus",$realtime);
|
||||||
|
FRAM_RSTATUS <= 1'h1; //Read Status
|
||||||
|
FRAM_go <= 1'h1; //Go
|
||||||
|
#10;
|
||||||
|
FRAM_RSTATUS <= 1'h0; //Read Status
|
||||||
|
FRAM_go <= 1'h0; //reset Go
|
||||||
|
$display("DEBUG: %0tns: Test_2_ReadStatus__-END",$realtime);
|
||||||
|
test_running <= 1'h0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task Test3();
|
||||||
|
test_running <= 1'h1;
|
||||||
|
$display("DEBUG: %0tns: Test_3_FRAM_WRITE",$realtime);
|
||||||
|
FRAM_Adr <= 20'h8FFF1; //Load 8FFF1 as adress
|
||||||
|
FRAM_DATA_IN <= 8'hAA; //Load AA as Data to Write into FRAM
|
||||||
|
FRAM_RW <= 1'h0; //Write Operation
|
||||||
|
FRAM_go <= 1'h1; //Go
|
||||||
|
#10;
|
||||||
|
FRAM_go <= 1'h0; //resetGo
|
||||||
|
$display("DEBUG: %0tns: Test_3_FRAM_WRITE__-END",$realtime);
|
||||||
|
test_running <= 1'h0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task Test4();
|
||||||
|
test_running <= 1'h1;
|
||||||
|
$display("DEBUG: %0tns: Test_4_FRAM_READ",$realtime);
|
||||||
|
FRAM_Adr <= 20'h8FFF1; //Load 8FFF1 as adress
|
||||||
|
FRAM_RW <= 1'h1; //Read
|
||||||
|
FRAM_go <= 1'h1; //Go
|
||||||
|
#10;
|
||||||
|
FRAM_go <= 1'h0; //resetGo
|
||||||
|
FRAM_RW <= 1'h0; //Read
|
||||||
|
$display("DEBUG: %0tns: Test_4_FRAM_READ__-END",$realtime);
|
||||||
|
test_running <= 1'h0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FRAM FRAM_ut(
|
||||||
|
.i_clk(clk),
|
||||||
|
.i_nreset(nReset),
|
||||||
|
.i_adr(FRAM_Adr),
|
||||||
|
.i_data(FRAM_DATA_IN),
|
||||||
|
.o_data(FRAM_DATA_OUT),
|
||||||
|
.i_rw(FRAM_RW),
|
||||||
|
.i_status(FRAM_RSTATUS),
|
||||||
|
.i_hbn(FRAM_hbn),
|
||||||
|
.i_cready(FRAM_go),
|
||||||
|
.o_busy(FRAM_busy),
|
||||||
|
|
||||||
|
.o_SPI_Clk(SPI_CLK),
|
||||||
|
.i_SPI_MISO(SPI_MOSI), // !!! only for Testing!!!
|
||||||
|
.o_SPI_MOSI(SPI_MOSI), //
|
||||||
|
.o_SPI_CS_n(SPI_CS)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user