verilog设计——SPI

2023-11-03

//spi_master
`timescale 1ns/1ps

module spi_master
#(
	parameter	CLK_FREQUENCE	= 50_000_000,	//system clk frequence
				SPI_FREQUENCE	= 5_000_000,	//spi clk frequence
				DATA_WIDTH		= 8,	        //serial word length
				CPOL			= 0,	        //SPI mode selection (mode 0 default)
				CPHA			= 0				//CPOL = clock polarity, CPHA = clock phase
)
(
	input								clk,	    //system clk
	input								rst_n,	    //system reset
	input		[DATA_WIDTH-1:0]		data_in,	//the data sent by mosi
	input								start,	    //spi transmission flag
	input								miso,	    //spi bus miso input
	output	reg							sclk,	    //spi bus sclk
	output	reg							cs_n,	    //spi bus slave select line
	output								mosi,	    //spi bus mosi output
	output	reg							finish,	    //SPI transmission finish flag
	output	reg [DATA_WIDTH-1:0]		data_out	//the data received by miso
);

localparam	FREQUENCE_CNT	= CLK_FREQUENCE/SPI_FREQUENCE - 1	,
			SHIFT_WIDTH		= log2(DATA_WIDTH)					,
			CNT_WIDTH		= log2(FREQUENCE_CNT)				;

localparam	IDLE	=	3'b000	,
			LOAD	=	3'b001	,
			SHIFT	=	3'b010	,
			DONE	=	3'b100	;

reg		[2:0]				cstate		;	//FSM current state
reg		[2:0]				nstate		;	//FSM next state
reg							clk_cnt_en	;	//start clk_cnt to generate sclk
reg							sclk_a		;	//sclk register to capture the edge of sclk
reg							sclk_b		;	//sclk register to capture the edge of sclk
wire						sclk_posedge;	//posedge of sclk
wire						sclk_negedge;	//negedge of sclk
wire						shift_en	;	//the signal to enable shift register to generate mosi
wire						sampl_en	;	//the signal to sample the data from miso
reg		[CNT_WIDTH-1:0]		clk_cnt		;	//the counter to generate sclk
reg		[SHIFT_WIDTH-1:0]	shift_cnt	;	//the counter to count the number of shifts
reg		[DATA_WIDTH-1:0]	data_reg	;	//the register to latch the data_in

//the counter to generate sclk
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        clk_cnt <= 'd0;
    else if(clk_cnt_en)
        if(clk_cnt == FREQUENCE_CNT)
            clk_cnt <= 'd0;
        else
            clk_cnt <= clk_cnt + 1'b1;
    else
        clk_cnt <= 'd0;
end

//generate sclk
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sclk <= CPOL;
    else if(clk_cnt_en)
        if(clk_cnt_en == FREQUENCE_CNT)
            sclk <= ~sclk;
        else
            sclk <= sclk;
    else
        sclk <= CPOL;
end

//sclk register
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sclk_a <= CPOL;
        sclk_b <= CPOL;
    end
    else if(clk_cnt_en) begin
        sclk_a <= sclk;
        sclk_b <= sclk_a;
    end
end

assign sclk_posedge = ~sclk_b & sclk_a
assign sclk_negedge = ~sclk_a & sclk_b

//generate blocks
generate
    case(CPHA)
        0:assign sample_en = sclk_posedge;
        1:assign sample_en = sclk_negedge;
        default:assign sample_en = sclk_posedge;
    endcase
endgenerate

generate
    case(CPHA)
        0:assign shift_en = sclk_negedge;
        1:assign shift_en = sclk_posedge;
        default:assign shift_en = sclk_posedge;
    endcase
endgenerate

//FSM_1
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cstate <= IDLE;
    else
        cstate <= nstate;
end

//FSM_2
always@(*) begin
    case (cstate)
        IDLE:nstate = start?load:IDLE;
        LOAD:nstate = SHIFT;
        SHIFT:nstate = (shift_cnt == DATA_WIDTH)?DONE:SHIFT;
        DONE:nstate = IDLE;
    endcase
end

//FSM_3
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        clk_cnt_en <= 1'b0;
        data_reg   <= 'd0;
        cs_n       <= 1'b1;
        shift_cnt  <= 'd0;
        finish     <= 1'b0;
    end
    else begin
        case(nstate)
            IDLE:begin
                clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				shift_cnt	<= 'd0	;
				finish 		<= 1'b0	;
			end
			LOAD	: begin
				clk_cnt_en	<= 1'b1		;
				data_reg	<= data_in	;
				cs_n		<= 1'b0		;
				shift_cnt	<= 'd0		;
				finish 		<= 1'b0		;
			end
			SHIFT	: begin
				if (shift_en) begin
					shift_cnt	<= shift_cnt + 1'b1 ;
					data_reg	<= {data_reg[DATA_WIDTH-2:0],1'b0};
				end else begin
					shift_cnt	<= shift_cnt	;
					data_reg	<= data_reg		;
				end
				clk_cnt_en	<= 1'b1	;
				cs_n		<= 1'b0	;
				finish 		<= 1'b0	;
			end
			DONE	: begin
				clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				data_reg	<= 'd0	;
				finish 		<= 1'b1	;
			end
			default	: begin
				clk_cnt_en	<= 1'b0	;
				data_reg	<= 'd0	;
				cs_n		<= 1'b1	;
				data_reg	<= 'd0	;
				finish 		<= 1'b0	;
			end
		endcase
	end
end

//mosi output MSB first
assign mosi = data_reg[DATA_WIDTH-1];
//sample data from the miso line
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_out <= 'd0;
	else if (sampl_en) 
		data_out <= {data_out[DATA_WIDTH-1:0],miso};
	else
		data_out <= data_out;
end

//the function to get the width of data 
function integer log2(input integer v);
  begin
	log2=0;
	while(v>>log2) 
	  log2=log2+1;
  end
endfunction
endmodule
//spi_slave
`timescale 1ns/1ps

module SPI_Slave
#(
	parameter	CLK_FREQUENCE	= 50_000_000		,	//system clk frequence
				SPI_FREQUENCE	= 5_000_000			,	//spi clk frequence
				DATA_WIDTH		= 8					,	//serial word length
				CPOL			= 1					,	//SPI mode selection (mode 0 default)
				CPHA			= 1					 	//CPOL = clock polarity, CPHA = clock phase
)
(
	input								clk			,	//system clk
	input								rst_n		,	//system reset
	input		[DATA_WIDTH-1:0]		data_in		,	//the data sent by miso
	input								sclk		,	//spi bus sclk
	input								cs_n		,	//spi bus slave select line
	input								mosi		,	//spi bus mosi input
	output								miso		,	//spi bus miso output
	output								data_valid	,	//the data received by mosi valid
	output	reg	[DATA_WIDTH-1:0]		data_out	 	//the data received by mosi,valid when data_valid is high
);

localparam	SFIFT_NUM = log2(DATA_WIDTH);

reg	[DATA_WIDTH-1:0]		data_reg	;	//the register to latch the data_in,also the shift register
reg	[ SFIFT_NUM-1:0]		sampl_num	;	//the counter to count the number of sample
reg							sclk_a		;	//sclk register to capture the edge of sclk
reg							sclk_b		;	//sclk register to capture the edge of sclk
wire						sclk_posedge;	//posedge of sclk
wire						sclk_negedge;	//negedge of sclk
reg							cs_n_a		;	//cs_n register to capture the edge of cs_n
reg							cs_n_b		;	//cs_n register to capture the edge of cs_n
wire						cs_n_negedge;	//negedge of cs_n to latch the data
wire						shift_en	;	//the signal to enable shift register to generate mosi
wire						sampl_en	;	//the signal to sample the data from miso
//------------------------------------------
//to capture the edge of sclk
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		sclk_a <= CPOL;
		sclk_b <= CPOL;
	end else if (!cs_n) begin
		sclk_a <= sclk;
		sclk_b <= sclk_a;
	end
end

assign sclk_posedge = ~sclk_b & sclk_a;
assign sclk_negedge = ~sclk_a & sclk_b;
//------------------------------------------
//to capture the edge of sclk
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		cs_n_a	<= 1'b1;
		cs_n_b	<= 1'b1;
	end else begin
		cs_n_a	<= cs_n		;
		cs_n_b	<= cs_n_a	;
	end
end

assign cs_n_negedge = ~cs_n_a & cs_n_b;
//------------------------------------------
//==============================================
//==============GENERATE BLOCKS=================
generate
	case (CPHA)
		0: assign sampl_en = sclk_posedge;
		1: assign sampl_en = sclk_negedge;
		default: assign sampl_en = sclk_posedge;
	endcase
endgenerate

generate
 	case (CPHA)
		0: assign shift_en = sclk_negedge;
 		1: assign shift_en = sclk_posedge;
		default: assign shift_en = sclk_posedge;
	endcase
endgenerate
//==================================================
//the register to latch the data_in
//also the shift register to generate the miso
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_reg <= 'd0;
	else if(cs_n_negedge)
		data_reg <= data_in;
	else if (!cs_n & shift_en) 
		data_reg <= {data_reg[DATA_WIDTH-2:0],1'b0};
	else
		data_reg <= data_reg;
end
//miso output MSB first
assign miso = !cs_n ? data_reg[DATA_WIDTH-1] : 1'd0;
//==================================================
//sample data from the mosi line
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		data_out <= 'd0;
	else if (!cs_n & sampl_en) 
		data_out <= {data_out[DATA_WIDTH-2:0],mosi};
	else
		data_out <= data_out;
end
//the counter to count the number of sampled data bit
always @(posedge clk or negedge rst_n) begin
	if (!rst_n) 
		sampl_num <= 'd0;
	else if (cs_n)
		sampl_num <= 'd0;
	else if (!cs_n & sampl_en) 
		if (sampl_num == DATA_WIDTH)
			sampl_num <= 'd1;
		else
			sampl_num <= sampl_num + 1'b1;
	else
		sampl_num <= sampl_num;
end
//the received data valid
assign data_valid = sampl_num == DATA_WIDTH;
//the function to get the width of data 
function integer log2(input integer v);
  begin
	log2=0;
	while(v>>log2) 
	  log2=log2+1;
  end
endfunction
endmodule

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

verilog设计——SPI 的相关文章

随机推荐

  • Pandas 使用指南

    Pandas 可以说是在 Python 数据科学领域应用最为广泛的工具之一 Pandas 是 Python 的核心数据分析支持库 提供了快速 灵活 明确的数据结构 旨在简单 直观地处理关系型 标记型数据 Pandas 的目标是成为 Pyth
  • PCB表面处理方式详解

    上一篇文章中介绍了关于PCB设计中的常用基本概念 那么本篇文章就挑选其中的第三点 表面处理 给大家展开说一说 顺便也自我温习一下 什么是表面处理 由于铜长期与空气接触会使得铜氧化 所以我们需要在PCB表面做一些处理 这样才可以保证PCB板的
  • JQuery Dialog(JS模态窗口,可拖拽的DIV)

    代码分析地址 http jingyan baidu com album fb48e8be5347156e622e14ac html 或者http jingyan baidu com article fb48e8be5347156e622e1
  • 升级log4j2遇到的那些坑

    升级log4j2遇到的那些坑
  • 数据分析与挖掘之一:流程

    概念 数据分析 Data Analysis 是以数据为分析对象 以探索数据内的有用信息为主要途径 以解决业务需求为最终目标 包含业务理 解 数据采集 数据清洗 数据探索 数据可视化 数据建模 模型结果可视化 分析结果的业务应用等步骤在内的一
  • vue中读取对象中对象的属性无法读取报错

    问题描述 报错内容 Vue warn Error in render TypeError Cannot read properties of undefined reading cnName 在写博客系统的时候 在文章详情页 需要获取文章中
  • 杂谈随感-6:敢问路在何方?

    不识庐山真面目 只缘生在此山中 人员远虑 必有近忧 敢问路在何方 4G还是5G 是 钻地道 还是 云中飘 3G 已死 没有出路 4G 行将入木 死路一条 5G Hub不死不活 僵尸 RRU与BBU 一个钻地道 一个云中飘 渐行渐远 各走各路
  • 常用Dos命令

    1 dos命令 color all 修改背景字体颜色 cls 清屏 dir 查看当前目录有哪些文件 a 查看隐藏文件 a d 只查看目录不显示文档 r 只读文件 A 准备存档的文件 在内存中写了但是没网硬盘里面写 S 系统文件 c 显示文内
  • Caffe 工程的一些编译错误以及解决方案

    CAFFE深度学习交流群 532629018 整理一下最近遇到caffe工程的一些编译错误以及解决方法 1 cuDNN cuDNN当前最新版本是v5 近两三年的一些caffe工程 使用的版本不尽相同 其中以v2 v3版本的最为常见 所以使用
  • UCenter安装时提示mysql_connect()不支持

    问题描述 安装时 提示mysql connect 不支持 请检查 mysql 模块是否正确加载 如下图 分析原因 查看php官方帮助文档得知 mysql connect是php4有php5中的函数 在php5 5 0已标记为废弃 在php7
  • python-numpy一些方法总结

    1 multiply 例子 x1 1 2 3 x2 4 5 6 print multiply x1 x2 输出 4 10 18 multiply函数得到的结果是对应位置上面的元素进行相乘 2 std 标准方差 var 方差 例子 b 1 3
  • C++继承时派生类与基类有同名函数时如何分别引用

    一 普通函数同名 当某个函数func 在基类和派生类中都有定义时 派生类中的函数func 将修改从基类继承来的函数func 如果非要从派生类中访问基类的函数func 有两种方法 定义基类指针 让基类指针指向派生类对象 则调用的是基类func
  • ns2报错

    若报以下错误 finish couldn t execute nam no such file or directory 最简单的解决方法是在命令行中到你的ns安装目录下 进入nam目录 输入 sudo make install
  • k8s部署SpringBoot项目

    一 前言 本文将介绍如何通过CICD将SpringBoot框架的Web项目发布到k8s集群中 文章中有使用到eureka的注册 如果对如何在k8s集群中部署eureka 那么可以参考本人的 k8s部署eureka集群 文章 如果只是为了测试
  • java常见笔试题目

    1 下列那一行代码编译后不会出现警告或错误 1 char c a 2 byte b 257 3 boolean b null 4 int i 10 5 float f 1 3 2 下面这段代码编译时会发生什么情况 public class
  • 基于光栅波导结构的 R AR&MR 系统的 建模

    增强现实和混合现实 AR MR 作为全新的头戴式显示概念 作为 5G 时代的一个核心应用 具有巨大的市场需求和潜力 其中一种典型的 AR MR 设备是基于光栅波导结构 而正是因为 光学光栅这种微纳元件的使用 我们不能简单地使用基于几何光学的
  • 无线充电技术

    在過去的百年之中 作為電器與插座之間的連線 電線已經成了一種習慣且不可或缺的存在 儘管無線充電技術在實驗室中已存在多年 卻始終因為需求不高而無法量產 不過 這一切將在不久的未來改變 一場以無線充電為主角的科技革命 正以近年來被廣泛使用的各種
  • 很漂亮的按钮css样式(没有用到图片,可直接拷贝代码使用)

    对于程序员 有时候也需要对页面风格做些改动 整体的页面风格还是美工的工作 按钮其实是程序员很常用的 如果美工没有设计好 那就自己设计吧 在网上发现有乐于奉献的人共享了代码 效果很好 而且没有使用到图片 这个很重要 如果你使用别人的css 里
  • 智能家居解决方案及企划书

    一 背景 随着科技的不断发展 智能家居已经成为了一种趋势 越来越多的人开始追求智能化 便捷化 舒适化的生活方式 智能家居市场也因此迅速崛起 本企划书旨在为智能家居市场提供一套完整的解决方案 帮助企业在竞争激烈的市场中占据一席之地 二 市场分
  • verilog设计——SPI

    spi master timescale 1ns 1ps module spi master parameter CLK FREQUENCE 50 000 000 system clk frequence SPI FREQUENCE 5 0