247 lines
8.1 KiB
Coq
247 lines
8.1 KiB
Coq
|
/*
|
|||
|
Copyright 2020 Blue Liang, liangkangnan@163.com
|
|||
|
|
|||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|||
|
you may not use this file except in compliance with the License.
|
|||
|
You may obtain a copy of the License at
|
|||
|
|
|||
|
http://www.apache.org/licenses/LICENSE-2.0
|
|||
|
|
|||
|
Unless required by applicable law or agreed to in writing, software
|
|||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
|
See the License for the specific language governing permissions and
|
|||
|
limitations under the License.
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
// spi master模块
|
|||
|
module spi(
|
|||
|
|
|||
|
input wire clk,
|
|||
|
input wire rst,
|
|||
|
|
|||
|
input wire[31:0] data_i,
|
|||
|
input wire[31:0] addr_i,
|
|||
|
input wire we_i,
|
|||
|
input wire req_i,
|
|||
|
|
|||
|
output reg[31:0] data_o,
|
|||
|
output reg ack_o,
|
|||
|
|
|||
|
output reg spi_mosi, // spi控制器输出、spi设备输入信号
|
|||
|
input wire spi_miso, // spi控制器输入、spi设备输出信号
|
|||
|
output wire spi_ss, // spi设备片选
|
|||
|
output reg spi_clk // spi设备时钟,最大频率为输入clk的一半
|
|||
|
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
localparam SPI_CTRL = 4'h0; // spi_ctrl寄存器地址偏移
|
|||
|
localparam SPI_DATA = 4'h4; // spi_data寄存器地址偏移
|
|||
|
localparam SPI_STATUS = 4'h8; // spi_status寄存器地址偏移
|
|||
|
|
|||
|
// spi控制寄存器
|
|||
|
// addr: 0x00
|
|||
|
// [0]: 1: enable, 0: disable
|
|||
|
// [1]: CPOL
|
|||
|
// [2]: CPHA
|
|||
|
// [3]: select slave, 1: select, 0: deselect
|
|||
|
// [15:8]: clk div
|
|||
|
reg[31:0] spi_ctrl;
|
|||
|
// spi数据寄存器
|
|||
|
// addr: 0x04
|
|||
|
// [7:0] cmd or inout data
|
|||
|
reg[31:0] spi_data;
|
|||
|
// spi状态寄存器
|
|||
|
// addr: 0x08
|
|||
|
// [0]: 1: busy, 0: idle
|
|||
|
reg[31:0] spi_status;
|
|||
|
|
|||
|
reg[8:0] clk_cnt; // 分频计数
|
|||
|
reg en; // 使能,开始传输信号,传输期间一直有效
|
|||
|
reg[4:0] spi_clk_edge_cnt; // spi clk时钟沿的个数
|
|||
|
reg spi_clk_edge_level; // spi clk沿电平
|
|||
|
reg[7:0] rdata; // 从spi设备读回来的数据
|
|||
|
reg done; // 传输完成信号
|
|||
|
reg[3:0] bit_index; // 数据bit索引
|
|||
|
wire[8:0] div_cnt;
|
|||
|
|
|||
|
|
|||
|
assign spi_ss = ~spi_ctrl[3]; // SPI设备片选信号
|
|||
|
|
|||
|
assign div_cnt = spi_ctrl[15:8];// 0: 2分频,1:4分频,2:8分频,3:16分频,4:32分频,以此类推
|
|||
|
|
|||
|
|
|||
|
// 产生使能信号
|
|||
|
// 传输期间一直有效
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
en <= 1'b0;
|
|||
|
end else begin
|
|||
|
if (spi_ctrl[0] == 1'b1) begin
|
|||
|
en <= 1'b1;
|
|||
|
end else if (done == 1'b1) begin
|
|||
|
en <= 1'b0;
|
|||
|
end else begin
|
|||
|
en <= en;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// 对输入时钟进行计数
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
clk_cnt <= 9'h0;
|
|||
|
end else if (en == 1'b1) begin
|
|||
|
if (clk_cnt == div_cnt) begin
|
|||
|
clk_cnt <= 9'h0;
|
|||
|
end else begin
|
|||
|
clk_cnt <= clk_cnt + 1'b1;
|
|||
|
end
|
|||
|
end else begin
|
|||
|
clk_cnt <= 9'h0;
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// 对spi clk沿进行计数
|
|||
|
// 每当计数到分频值时产生一个上升沿脉冲
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
spi_clk_edge_cnt <= 5'h0;
|
|||
|
spi_clk_edge_level <= 1'b0;
|
|||
|
end else if (en == 1'b1) begin
|
|||
|
// 计数达到分频值
|
|||
|
if (clk_cnt == div_cnt) begin
|
|||
|
if (spi_clk_edge_cnt == 5'd17) begin
|
|||
|
spi_clk_edge_cnt <= 5'h0;
|
|||
|
spi_clk_edge_level <= 1'b0;
|
|||
|
end else begin
|
|||
|
spi_clk_edge_cnt <= spi_clk_edge_cnt + 1'b1;
|
|||
|
spi_clk_edge_level <= 1'b1;
|
|||
|
end
|
|||
|
end else begin
|
|||
|
spi_clk_edge_level <= 1'b0;
|
|||
|
end
|
|||
|
end else begin
|
|||
|
spi_clk_edge_cnt <= 5'h0;
|
|||
|
spi_clk_edge_level <= 1'b0;
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// bit序列
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
spi_clk <= 1'b0;
|
|||
|
rdata <= 8'h0;
|
|||
|
spi_mosi <= 1'b0;
|
|||
|
bit_index <= 4'h0;
|
|||
|
end else begin
|
|||
|
if (en) begin
|
|||
|
if (spi_clk_edge_level == 1'b1) begin
|
|||
|
case (spi_clk_edge_cnt)
|
|||
|
// 第奇数个时钟沿
|
|||
|
1, 3, 5, 7, 9, 11, 13, 15: begin
|
|||
|
spi_clk <= ~spi_clk;
|
|||
|
if (spi_ctrl[2] == 1'b1) begin
|
|||
|
spi_mosi <= spi_data[bit_index]; // 送出1bit数据
|
|||
|
bit_index <= bit_index - 1'b1;
|
|||
|
end else begin
|
|||
|
rdata <= {rdata[6:0], spi_miso}; // 读1bit数据
|
|||
|
end
|
|||
|
end
|
|||
|
// 第偶数个时钟沿
|
|||
|
2, 4, 6, 8, 10, 12, 14, 16: begin
|
|||
|
spi_clk <= ~spi_clk;
|
|||
|
if (spi_ctrl[2] == 1'b1) begin
|
|||
|
rdata <= {rdata[6:0], spi_miso}; // 读1bit数据
|
|||
|
end else begin
|
|||
|
spi_mosi <= spi_data[bit_index]; // 送出1bit数据
|
|||
|
bit_index <= bit_index - 1'b1;
|
|||
|
end
|
|||
|
end
|
|||
|
17: begin
|
|||
|
spi_clk <= spi_ctrl[1];
|
|||
|
end
|
|||
|
endcase
|
|||
|
end
|
|||
|
end else begin
|
|||
|
// 初始状态
|
|||
|
spi_clk <= spi_ctrl[1];
|
|||
|
if (spi_ctrl[2] == 1'b0) begin
|
|||
|
spi_mosi <= spi_data[7]; // 送出最高位数据
|
|||
|
bit_index <= 4'h6;
|
|||
|
end else begin
|
|||
|
bit_index <= 4'h7;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// 产生结束(完成)信号
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
done <= 1'b0;
|
|||
|
end else begin
|
|||
|
if (en && spi_clk_edge_cnt == 5'd17) begin
|
|||
|
done <= 1'b1;
|
|||
|
end else begin
|
|||
|
done <= 1'b0;
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// write reg
|
|||
|
always @ (posedge clk) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
spi_ctrl <= 32'h0;
|
|||
|
spi_data <= 32'h0;
|
|||
|
spi_status <= 32'h0;
|
|||
|
end else begin
|
|||
|
spi_status[0] <= en;
|
|||
|
if (we_i == 1'b1) begin
|
|||
|
case (addr_i[3:0])
|
|||
|
SPI_CTRL: begin
|
|||
|
spi_ctrl <= data_i;
|
|||
|
end
|
|||
|
SPI_DATA: begin
|
|||
|
spi_data <= data_i;
|
|||
|
end
|
|||
|
default: begin
|
|||
|
|
|||
|
end
|
|||
|
endcase
|
|||
|
end else begin
|
|||
|
spi_ctrl[0] <= 1'b0;
|
|||
|
// 发送完成后更新数据寄存器
|
|||
|
if (done == 1'b1) begin
|
|||
|
spi_data <= {24'h0, rdata};
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
// read reg
|
|||
|
always @ (*) begin
|
|||
|
if (rst == 1'b0) begin
|
|||
|
data_o = 32'h0;
|
|||
|
end else begin
|
|||
|
case (addr_i[3:0])
|
|||
|
SPI_CTRL: begin
|
|||
|
data_o = spi_ctrl;
|
|||
|
end
|
|||
|
SPI_DATA: begin
|
|||
|
data_o = spi_data;
|
|||
|
end
|
|||
|
SPI_STATUS: begin
|
|||
|
data_o = spi_status;
|
|||
|
end
|
|||
|
default: begin
|
|||
|
data_o = 32'h0;
|
|||
|
end
|
|||
|
endcase
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
endmodule
|