基于AHB协议的SRAM控制器的Verilog实现(详解代码)(一)——sram_core的实现

这篇具有很好参考价值的文章主要介绍了基于AHB协议的SRAM控制器的Verilog实现(详解代码)(一)——sram_core的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

 一、sramc(sram_controller)的总框架和模块划分

Features of sramc

结构框图和信号描述

二、sram_core的Verilog实现

动手开敲前的构思

Verilog代码和代码分析

三、sram_core的仿真验证

下载链接


 一、sramc(sram_controller)的总框架和模块划分

sram因为其高读写速度常常作为CPU和嵌入式IC的缓存,是一个数字系统中必不可少的存在,因此能够支持各种总线协议的sram控制器也是必不可少的。本系列文章将完成一个支持AHB总线协议的sram控制器的Verilog实现,并对Verilog代码进行详细分析。本篇文章主要完成了sramc的一个子模块——sram_core的Verilog实现。

Features of sramc

思路清晰,先说功能:

  • 能按照AHB协议的读写时序进行数据传输;
  • 能够实现流水线形式的单周期读写;
  • 支持8、16、32位数据传输;
  • 对于一次传输中用不到的sram块,不对其使能;

结构框图和信号描述

将sramc划分为两个子模块:ahb_sramc_if和sram_core。ahb_sramc_if是sram与AHB总线的接口,负责根据协议时序为sram_core提供使能读写控制信号,并对总线地址haddr进行处理提供给sram_core;sram_core负责管理各sram bank的使能信号,并处理写入数据和读出数据。

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

signal I/O width descriptions
hrstn input 1 总线复位信号,同步复位,低有效
hclk input 1 总线时钟
hsel_sram input 1 sramc选中信号
hready input 1 总线可用信号,从机只能在hready为1时从总线采样
hwrite input 1 读写控制信号1写0读
htrans input 2 传输类型,详见AHB协议
hsize input 3 传输位宽,详见AHB协议
haddr input AHB_ADDR_WIDTH 总线地址
hwdata input AHB_DATA_WIDTH 总线写数据
hreadyout output 1 从机传输是否完成
hresp output 1 从机传输状态应答信号
hrdata output AHB_DATA_WIDTH 读数据

二、sram_core的Verilog实现

首先要进一步细化sram_core要实现的功能:

  • 支持8、16、32位的数据传输;
  • 对于一次传输中用不到的sram块,不对其使能;
  • 对sramc的私有地址(private_addr)进行处理,确保每个sram bank收到的地址是sram bank中与private_addr对应的地址;
  • 对要写入的数据进行位重排列,确保正确的数据写入正确的sram bank里;
  • 对读出的数据进行位排列,确保从相应sram bank读出的数据位于输出hrdata的正确位上

乍一看可能会懵,不知道这些功能具体指什么。但听我说,先别急,后面我会尽可能的展开说明。

动手开敲前的构思

首先最多要支持32位的数据传输,并且要支持单周期传输 ,而sram一般是8位的,这就决定了sram_core最少要包含4个sram bank。出于设计难度的考虑,本设计决定使用了4个sram bank,但如果sram比较大,则需增加bank数,使得每个sram bank的大小不超过一定水平。

既然要使用多个sram bank,那就不得不考虑怎样对每个sram bank进行地址分配。同样由于最多要支持32位传输,所以会出现4个bank同时工作的情况,这就决定了只能按照这样的方式分配地址:private_addr 0是sram bank0的地址0,private_addr 1是sram bank1的地址0,private_addr 2是sram bank2的地址0,private_addr 3是sram bank3的地址0,private_addr 4是sram bank0的地址1,private_addr 5是sram bank1的地址1......以此类推。

既然要进行地址分配,那就不得不对private_addr进行处理,确保每个sram bank收到的地址是sram bank中按照上述地址分配方式与private_addr对应的地址。所以要有一个地址处理逻辑块。

由于要使用多个sram bank且要按照上述方式进行地址分配,所以不能将hwdata按照从高到低位直接接入到各sram bank=中,对于hrdata同样如此。考虑下这种情况:总线上传来了对地址2的16位的读请求,sram_core给出的正确数据应该是{16’h0,sram3, sram2},但如果将各sram bank的读出数据直接按照位从高到低接到hrdata上,那么sram_core给出的数据将会是{sram3,sram2,sram1,sram0}。所以要有写数据位重排逻辑和读数据位排列逻辑来处理hwdata和从各sram bank中读出的数据。

另外对于一次传输中用不到的sram块,不对其使能,所以还要有一个sram块使能控制逻辑。

经过以上构思可大致确定sram_core的框架,其框图和信号描述如下所示:

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

signal I/O width descriptions
hclk input 1 总线时钟
hsel_sram input 1 sram_core使能信号
write input 1 写控制信号,1有效
read input 1 读控制信号,1有效
hsize input 3 传输位宽,详见AHB协议
private_addr input PRIVATE_ADDR_WIDTH

sramc私有地址

private_addr_reg1 input PRIVATE_ADDR_WIDTH 寄存一拍的private_addr
hwdata input AHB_DATA_WIDTH 总线写数据
hrdata output AHB_DATA_WIDTH 读数据

Verilog代码和代码分析

由于我搞不定Memory compiler所以我就自己写了个sram,对于Verilog行为级设计也够用了,虽然有write和read端,但是个单端口sram,不能同时读写。先附上sram.v:

`timescale 1 ns/1 ns
module sram
#(parameter SRAM_ADDR_WIDTH = 6)
(
	clk, 
	ce,
	write,
	read,
	addr,
	wrdata,
	rddata
);

	localparam SRAM_DEPTH = 1 << SRAM_ADDR_WIDTH; 	
	
	input clk;
	input ce;
	input write;
	input read;
	input[SRAM_ADDR_WIDTH-1:0] addr;
	input[7:0] wrdata;
	output reg[7:0] rddata;

	reg[7:0] mem[0:SRAM_DEPTH-1];

	always@(posedge clk) begin //写
		if(ce&&write)
			mem[addr] = wrdata;
	end

	always@(posedge clk) begin //读
		if(ce&&read)
			rddata = mem[addr];
	end

endmodule

sram_core.v如下所示:

`timescale 1 ns/1 ns
module sram_core
#(parameter AHB_ADDR_WIDTH = 32, AHB_DATA_WIDTH = 32, BANK_NUM = 4, SRAM_ADDR_WIDTH = 6)
(
	clk, 
	hsel_sram,
	write,
	read,
	hsize,
	private_addr,
	private_addr_reg1,
	hwdata,
	hrdata
);
	
	input clk;
	input hsel_sram;
	input write;
	input read;
	input[2:0] hsize;
	input[AHB_ADDR_WIDTH-1:0] private_addr;
	input[AHB_ADDR_WIDTH-1:0] private_addr_reg1;//对于写操作,写入是在地址周期的下一周期发生,另外读出的数据需要维持一个周期,所以地址要寄存一拍
	input[AHB_DATA_WIDTH-1:0] hwdata;
	output[AHB_DATA_WIDTH-1:0] hrdata;
	
	
	wire[AHB_ADDR_WIDTH-1:0] sram_addr;
	reg[BANK_NUM-1:0] ce; //各SRAM BANK的使能信号
	wire[AHB_DATA_WIDTH-1:0] rddata_raw; //直接从各SRAM BANK中出来的读数据,其位顺序需要经过处理
	reg[AHB_DATA_WIDTH-1:0] wrdata_processed; //由hwdata经过处理得到,可直接按照从高到低位写入各BANK
	reg[AHB_DATA_WIDTH-1:0] rddata_processed; //由rddata_raw处理得到,直接作为输出读数据

	assign hrdata = rddata_processed;
	
	assign sram_addr = write ? private_addr_reg1 : private_addr;	

/********************各BANK使能控制逻辑*******************/
	always@(*) begin
		if(!hsel_sram)
			ce = 0;
		else begin
			case(hsize)
			  3'b000: begin //如果传输位宽是8位,一次只使能一个BANK
			    case(sram_addr[1:0])
			      2'b00: ce = 4'b0001;
			      2'b01: ce = 4'b0010;
			      2'b10: ce = 4'b0100;
			      2'b11: ce = 4'b1000;
			    endcase
			  end

			  3'b001: begin //如果传输位宽是16位,一次使能2个BANK
			    case(sram_addr[1:0])
			      2'b00: ce = 4'b0011;
			      2'b10: ce = 4'b1100;
			      default: ce = 4'b0000;
			    endcase
			  end

			  3'b010: ce = 4'b1111;//如果传输位宽是32位,一次使能4个BANK

			  default: ce = 4'b0000;
			endcase
		end			
	end

/********************写入数据和从各SRAM BANK读出的数据位重排列组合逻辑*******************/
	always@(*) begin
		if(hsel_sram) begin
			case(hsize)
			  3'b000: begin
			    case(private_addr_reg1[1:0])//对于写操作,写入是在地址周期的下一周期发生,另外读出的数据需要维持一个周期,所以地址要寄存一拍
			      2'b00: begin
				if(write)
				  wrdata_processed[7:0] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0 ,rddata_raw[7:0]};
			      end
			      2'b01: begin
				if(write)
				  wrdata_processed[15:8] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[15:8]};
			      end
			      2'b10: begin
				if(write)
				  wrdata_processed[23:16] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[23:16]};
			      end
			      2'b11: begin
				if(write)
				  wrdata_processed[31:24] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[31:24]};
			      end
			    endcase
			  end

			  3'b001: begin
			    case(private_addr_reg1[1:0])
			      2'b00: begin
				if(write)
				  wrdata_processed[15:0] = hwdata[15:0]; 
				if(read)
				  rddata_processed = {16'd0, rddata_raw[15:0]};
			      end
			      2'b10: begin
				if(write)
				  wrdata_processed[31:16] = hwdata[15:0]; 
				if(read)
				  rddata_processed = {16'd0, rddata_raw[31:16]};
			      end
			    endcase
			  end

			  3'b010: begin
			    if(write)
			      wrdata_processed = hwdata;
			    if(read)
			      rddata_processed = rddata_raw;
			  end
			endcase
		end
	end		

	genvar i;
	generate 
		for(i=0; i<BANK_NUM; i=i+1) begin: sram_instance
			sram #(.SRAM_ADDR_WIDTH(SRAM_ADDR_WIDTH)) 
			sram(
				.clk(clk), 
				.ce(ce[i]),
				.write(write),
				.read(read),
				.addr(sram_addr[SRAM_ADDR_WIDTH+1:2]),
				//总共有4个BANK,且必须要采用开头描述的地址分配,另外ahb协议有硬性要求,
				//传输地址必须与位宽对齐,所以总地址的去掉最低2位就可直接作为各BANK的地址
				.wrdata(wrdata_processed[8*i+7:8*i]),
				.rddata(rddata_raw[8*i+7:8*i])
			);			
		end
	endgenerate

endmodule

sram.v就是一个很基础的memory,没啥好说的,直接来看sram_core。

	input[AHB_ADDR_WIDTH-1:0] private_addr;
	input[AHB_ADDR_WIDTH-1:0] private_addr_reg1;

先说这两个地址信号,了解过微机原理都知道,一个单片机是需要对其上挂载的所有从机分配地址的,大部分情况下总线地址是不能直接用的,需要转化为从机的私有地址,private_addr就是sramc的私有地址,private_addr寄存一拍得到private_addr_reg1。这些工作决定交给ahb_sramc_if来做。

assign sram_addr = write ? private_addr_reg1 : private_addr;	

sram_addr是直接传给sram bank的地址,为什么写操作要用寄存一拍的地址呢?上时序图: 

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发 sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

 上图是ahb协议的基础的读写时序,可以看出ahb协议的数据传输是有两个阶段的:第一个地址周期要提供有效的控制和地址信号,第二个数据周期要进行数据采样。对于读操作,从机采样到数据和控制信号后就要立即开始给出并维持住数据,直到下一个上升沿总线采样完成,所以读操作直接用当前地址即可。而对于写操作,写入数据在从机采样地址的下一个周期才提供,写入操作真正发生在图中的第三个上升沿,而由于流水线工作方式,总线地址已经发生改变,所以写操作要用寄存一拍的地址。

/********************各BANK使能控制逻辑*******************/
	always@(*) begin
		if(!hsel_sram)
			ce = 0;
		else begin
			case(hsize)
			  3'b000: begin //如果传输位宽是8位,一次只使能一个BANK
			    case(sram_addr[1:0])
			      2'b00: ce = 4'b0001;
			      2'b01: ce = 4'b0010;
			      2'b10: ce = 4'b0100;
			      2'b11: ce = 4'b1000;
			    endcase
			  end

			  3'b001: begin //如果传输位宽是16位,一次使能2个BANK
			    case(sram_addr[1:0])
			      2'b00: ce = 4'b0011;
			      2'b10: ce = 4'b1100;
			      default: ce = 4'b0000;
			    endcase
			  end

			  3'b010: ce = 4'b1111;//如果传输位宽是32位,一次使能4个BANK

			  default: ce = 4'b0000;
			endcase
		end			
	end

接下来是各sram bank的使能控制逻辑,实现这个功能的思路简单直白,就是根据地址和传输位宽使能对应的sram bank,没什么好说的。注意这里只使用了sram_addr[1:0],因为只用了4个bank,所以只需地址的最低两位就足以判断此次传输要用到那些bank。

现在考虑一下这种情况:总线发起了一个对地址3的16位的传输请求。看一下我们的使能控制逻辑,发现在这种情况下竟然是所有sram bank都不使能,这岂不是一个大BUG?听我说,先别急,ahb协议里有这样一句话:

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

 意思就是传输地址必须要与传输位宽对齐,比如说16位传输的传输地址的最低位必须是0。所以说上述情况是不应该出现的,如果出现的话应当视作错误来处理,这个任务应该交给ahb_sramc_if来处理。大家可以思考一下为什么ahb协议要这样规定。我在敲代码时的感受就是这个规定最起码简化了设计。

/********************写入数据和从各SRAM BANK读出的数据位重排列组合逻辑*******************/
	always@(*) begin
		if(hsel_sram) begin
			case(hsize)
			  3'b000: begin
			    case(private_addr_reg1[1:0])//对于写操作,写入是在地址周期的下一周期发生,另外读出的数据需要维持一个周期,所以地址要寄存一拍
			      2'b00: begin
				if(write)
				  wrdata_processed[7:0] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0 ,rddata_raw[7:0]};
			      end
			      2'b01: begin
				if(write)
				  wrdata_processed[15:8] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[15:8]};
			      end
			      2'b10: begin
				if(write)
				  wrdata_processed[23:16] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[23:16]};
			      end
			      2'b11: begin
				if(write)
				  wrdata_processed[31:24] = hwdata[7:0]; 
				if(read)
				  rddata_processed = {24'd0, rddata_raw[31:24]};
			      end
			    endcase
			  end

			  3'b001: begin
			    case(private_addr_reg1[1:0])
			      2'b00: begin
				if(write)
				  wrdata_processed[15:0] = hwdata[15:0]; 
				if(read)
				  rddata_processed = {16'd0, rddata_raw[15:0]};
			      end
			      2'b10: begin
				if(write)
				  wrdata_processed[31:16] = hwdata[15:0]; 
				if(read)
				  rddata_processed = {16'd0, rddata_raw[31:16]};
			      end
			    endcase
			  end

			  3'b010: begin
			    if(write)
			      wrdata_processed = hwdata;
			    if(read)
			      rddata_processed = rddata_raw;
			  end
			endcase
		end
	end		

接下来是写入数据和读出数据处理逻辑,看着很长一段,但其实同样简单直白,就是根据地址和传输位宽将对应数据放到对应位置上。需要注意的是,这里直接就使用了寄存一拍的地址private_addr_reg1而不是sram_addr,对于写操作这样处理可以理解,为什么读操作也要这样呢?还记得前面在分析ahb读写时序时说过:读数据要维持到总线数据采样结束。如果在这里使用sram_addr我们看看会发生什么: 

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

可以看到,本应该维持一个周期的正确hrdata竟然变成了一闪而过的毛刺,观察此时的private_addr可找到原因:在110时刻的上升沿地址是0,数据处理逻辑按照地址0来处理数据,但由于流水线机制,这个上升沿之后地址变成了1,而数据处理逻辑是个组合逻辑,会马上按照地址1处理数据,所以地址0的hrdata就会一闪而过。使用private_addr_reg1就能避免这种情况。

	genvar i;
	generate 
		for(i=0; i<BANK_NUM; i=i+1) begin: sram_instance
			sram #(.SRAM_ADDR_WIDTH(SRAM_ADDR_WIDTH)) 
			sram(
				.clk(clk), 
				.ce(ce[i]),
				.write(write),
				.read(read),
				.addr(sram_addr[SRAM_ADDR_WIDTH+1:2]),
				//总共有4个BANK,且必须要采用开头描述的地址分配,另外ahb协议有硬性要求,
				//传输地址必须与位宽对齐,所以总地址的去掉最低2位就可直接作为各BANK的地址
				.wrdata(wrdata_processed[8*i+7:8*i]),
				.rddata(rddata_raw[8*i+7:8*i])
			);			
		end
	endgenerate

最后是sram的例化,此处还有一点要说明:注意传给sram的addr是sram_addr去掉了最低两位。还记得我们采用的地址分配方式吗,因为有4个bank,所以每个sram bank中与私有地址对应的地址就是sram_addr1掉最低两位。

三、sram_core的仿真验证

先附上仿真代码:

`timescale 1 ns/1 ns
module sram_core_test();

	parameter ADDR_WIDTH = 32, DATA_WIDTH = 32;	  

	reg clk, hsel_sram, write, read;
	reg[2:0] hsize;
	reg[ADDR_WIDTH-1:0] private_addr;
	reg[ADDR_WIDTH-1:0] private_addr_reg1;
	reg[DATA_WIDTH-1:0] hwdata;
	wire[DATA_WIDTH-1:0] hrdata;

	integer i;

	sram_core sram_core1 (
		.clk(clk), 
		.hsel_sram(hsel_sram),
		.write(write),
		.read(read),
		.hsize(hsize),
		.private_addr(private_addr),
		.private_addr_reg1(private_addr_reg1),
		.hwdata(hwdata),
		.hrdata(hrdata)
	);

	always #5 clk = ~clk;  
	
	always@(posedge clk)
		private_addr_reg1 <= private_addr;
	
	initial begin
		clk=1; write=1; read=0; hsel_sram=1; hsize=3'd0;
	
		#11 write=1; read=0; hsel_sram=1; hsize=0; 
		private_addr=32'h0000_0000; hwdata=32'h0000_0000;
		for(i=0; i<8; i=i+1) begin: write_8bytes_0
			#10 private_addr = private_addr + 1; hwdata = hwdata + 1;	 
		end
		
		#10 write=0; read=1; hsel_sram=1; hsize=3'd0; 
		private_addr=32'h0000_0000; 
		for(i=0; i<8; i=i+1) begin: read_8bytes
			#10 private_addr = private_addr + 1;
				 
		end

		#10 write=1; read=0; hsel_sram=1; hsize=3'd1; 
		private_addr=32'd0000_0000; hwdata=32'h0000_ffff;
		for(i=0; i<4; i=i+1) begin: write_4halfwords_1
			#10 private_addr = private_addr + 2; hwdata = hwdata + 1;	 
		end	

		#10 write=0; read=1; hsel_sram=1; hsize=3'd1; 
		private_addr=32'h0000_0000; 
		for(i=0; i<4; i=i+1) begin: read_4halfwords
			#10 private_addr = private_addr + 2;
		end

		#10 write=1; read=0; hsel_sram=1; hsize=3'd2; 
		private_addr=32'h0000_0000; hwdata=32'h0000_0000;
		for(i=0; i<2; i=i+1) begin: write_2words_0
			#10 private_addr = private_addr + 4; hwdata = hwdata + 1;	 
		end

		#10 write=0; read=1; hsel_sram=1; hsize=3'd2; 
		private_addr=32'h0000_0000; 
		for(i=0; i<2; i=i+1) begin: read_2words
			#10 private_addr = private_addr + 4;	 
		end

		#100 $finish;	

	end

	initial begin 
   		$fsdbDumpfile("sramc_test.fsdb");
   		$fsdbDumpvars(0);
	end 

endmodule

仿真代码就是地址从0开始,先依次进行8个8位的写操作,然后再依次进行8个8位的读操作,接着是16位先写后读,最后是32位的先写后读。这一个验证肯定不够,我其实做了很多仿真,但没有保存仿真代码,另外我没深入学习过验证,仿真代码不成体系,可以看到我连个task都没写,所以就不班门弄斧的讲仿真代码了。

sram协议,数字IC设计学习,fpga开发,嵌入式硬件,arm,arm开发

 这是仿真结果,你们看起来可能会迷,但我很清楚是没问题的。

下载链接

最后附上代码和ahb协议手册下载链接,在设计基于ahb协议的模块时,一定要先仔细看协议手册

https://download.csdn.net/download/qq_44055255/86479698文章来源地址https://www.toymoban.com/news/detail-792228.html

到了这里,关于基于AHB协议的SRAM控制器的Verilog实现(详解代码)(一)——sram_core的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于FPGA的4x4矩阵键盘控制器verilog开发实现

    欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果        基于FPGA的4x4矩阵键盘控制器是一种使用FPGA(现场可编程门阵列)来实现对4x4矩阵键盘进行控制的设备。该控制器能够有效地降低硬件资源的使用,提高系

    2024年02月11日
    浏览(51)
  • 【DDR】基于Verilog的DDR控制器的简单实现(一)——初始化

    在FPGA中,大规模数据的存储常常会用到DDR。为了方便用户使用,Xilinx提供了DDR MIG IP核,用户能够通过AXI接口进行DDR的读写访问,然而MIG内部自动实现了许多环节,不利于用户深入理解DDR的底层逻辑。 本文以美光(Micron)公司生产的DDR3芯片MT41J512M8RH-093为例,说明DDR芯片的操作

    2024年02月02日
    浏览(52)
  • m基于FPGA的FOC控制器verilog实现,包括CLARK,PARK,PID及SVPWM,含testbench

    目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件 Quartus II 12.1(64-Bit) ModelSim-Altera 6.6d Starter Edition 仿真结果如下: 整个系统的结构如下所示: 1、采集到两相电流 2、经过clarke变换后得到两轴正交电流量, 3、经过旋转变换后得到正交的电流量

    2024年02月15日
    浏览(54)
  • 基于FPGA的电风扇控制器verilog,视频/代码

    名称:基于FPGA的电风扇控制器verilog 软件:QuartusII 语言:Verilog 代码功能: 基于FPGA的电风扇控制器  运用 EDA SOPO实验开发系统设计一个基于FPGA的电风扇定时开关控制器,能实现手动和自动模式之间的切换。要求:  (1)KI为电源开关由电源开关控制电风扇的开关,即当K1为高电平“

    2024年02月07日
    浏览(55)
  • verilog实现I2C控制器 (小梅哥思路)----详细解析

    模块框图如下所示 输入输出信号: 整体的思路如下: 通过输入的命令组合,完成一次8字节数据的传输。定义了6种命令, WR 写数据请求 (6’b000_001) STA 起始位请求(6’b000_010) RD 读数据请求(6’b000_100) STO 停止位请求(6’b001_000) ACK 应答位请求(6’b010_000) NACK 无应答请求(

    2024年02月03日
    浏览(53)
  • 基于EDA的电烤箱控制器设计Verilog代码Quartus 新起点FPGA开发板

    名称:基于EDA的电烤箱控制器设计Verilog代码Quartus  新起点FPGA开发板(文末获取) 软件:Quartus 语言:Verilog 代码功能: 基于EDA的电烤箱控制器设计 主要研究内容 设计一个电烤箱控制器电路,要求具有三档加热功能,分别表示烧烤加热为低火、中火、高火。具有复位开关、启动

    2024年02月01日
    浏览(45)
  • 从零开始 verilog 以太网交换机(二)MAC接收控制器的设计与实现

    🔈声明: 😃博主主页:王_嘻嘻的CSDN主页 🧨 从零开始 verilog 以太网交换机系列专栏:点击这里 🔑未经作者允许,禁止转载,侵权必删 🚩关注本专题的朋友们可以收获一个经典交换机设计的全流程,包括设计与验证(FPGA);以太网MAC的基础知识。新手朋友们还将获得一个

    2024年01月19日
    浏览(56)
  • 基于Vivado软件实现电梯控制器仿真设计

    Contents 1设计目的及要求 2 1.1设计要求 2 1.2设计H的 2 2工作原理和系统框图 2 3各部分选定方案及电路组成、相关器件说明 2 3.1各部分选定方案 2 3.2相关器件说明 3 4调试过程 8 4.1调试步骤 8 4.2调试过程中出现的错误及修正方案 8 5功能测试 10 6设计结论 12 7设计心得与总结 12 7.1设

    2024年02月09日
    浏览(45)
  • 微波炉控制器Verilog代码Quartus仿真

    名称:微波炉控制器Verilog代码Quartus仿真(文末获取) 软件:Quartus 语言:Verilog 代码功能: 微波炉控制器 用芯片Altera Cyclone IV FPGA作为控制芯片,实现时间设置、温度设定、火力选择、声音提示,在硬件组成上,涉及到电源供电、按键输入、数码管显示、指示灯提示等。 由按

    2024年01月20日
    浏览(65)
  • 基于ROS实现的机器人运动PID控制器

    下面是一个基于ROS实现的机器人运动PID控制器的例子: 首先,需要定义机器人的运动控制器节点,例如: 其中, cmd_vel_pub 是一个发布器,用于发布机器人的运动控制指令; odom_sub 是一个订阅器,用于接收机器人的里程计信息。 然后,需要实现一个PID控制器的类,例如: 其

    2024年02月13日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包