RAM 的英文全称是 Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM 主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。本章我们将对 Vivado 软件生成的 RAM IP 核进行读写测试,并向大家介绍 Xilinx RAM IP 核的使用方法。
Xilinx 7 系列器件具有嵌入式存储器结构,满足了设计对片上存储器的需求。嵌入式存储器结构由一列列 BRAM(块 RAM)存储器模块组成,通过对这些 BRAM 存储器模块进行配置,可以实现各种存储器的功能,例如:RAM、移位寄存器、ROM 以及 FIFO 缓冲器。
Vivado 软件自带了 BMG IP 核(Block Memory Generator,块 RAM 生成器),可以配置成 RAM 或者ROM。这两者的区别是 RAM 是一种随机存取存储器,不仅仅可以存储数据,同时支持对存储的数据进行修改;而 ROM 是一种只读存储器,也就是说,在正常工作时只能读出数据,而不能写入数据。需要注意的是,配置成 RAM 或者 ROM 使用的资源都是 FPGA 内部的 BRAM,只不过配置成 ROM 时只用到了嵌入式 BRAM 的读数据端口。本章我们主要介绍通过 BRAM IP 核配置成 RAM 的使用方法。
Xilinx 7 系列器件内部的 BRAM 全部是真双端口 RAM(True Dual-Port ram,TDP),这两个端口都可以独立地对 BRAM 进行读/写。但也可以被配置成伪双端口 RAM(Simple Dual-Port ram,SDP)(有两个端口,但是其中一个只能读,另一个只能写)或单端口 RAM(只有一个端口,读/写只能通过这一个端口来进行)。单端口 RAM 只有一组数据总线、地址总线、时钟信号以及其他控制信号,而双端口 RAM 具有两 组数据总线、地址总线、时钟信号以及其他控制信号。有关 BRAM 的更详细的介绍,请读者参阅 Xilinx 官方的手册文档“UG473,7 Series FPGAs Memory Resources User Guide”。
单端口 RAM 类型和双端口 RAM 类型在操作上都是一样的,我们只要学会了单端口 RAM 的使用,那么学习双端口 RAM 的读写操作也是非常容易的。本章我们以配置成单端口 RAM 为例进行讲解。
BMG IP 核配置成单端口 RAM 的框图如下图所示。

各个端口的功能描述如下:
DINA:RAM 端口 A 写数据信号。
ADDRA:RAM 端口 A 读写地址信号,对于单端口 RAM 来说,读地址和写地址共用同该地址线。
本节实验任务是使用 Xilinx BMG IP 核,配置成一个单端口的 RAM,然后对 RAM 进行读写操作,通 过在 Vivado 自带的仿真器中观察波形是否正确,最后将设计下载到领航者 Zynq 开发板中,并使用 ILA 对其进行在线调试观察。
本实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设,各端口信号的管脚分配如下表所示:

对应的 XDC 约束语句如下所示:
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]



Component Name:设置该 IP 核的名称,这里保持默认即可。

Write Width:端口 A 写数据位宽,单位 Bit,这里设置成 8。


之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该 IP 核对应的 run“blk_mem_gen_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。

在其 Out-of-Context 综合的过程中,我们就可以进行 RTL 编码了。首先打开 IP 核的例化模板,在“Source” 窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“blk_mem_gen_0”-“Instantitation Template”, 我们可以看到“blk_mem_gen_0.veo”文件,它是由 IP 核自动生成的只读的 verilog 例化模板文件,双击就可以打开它,如下图所示。

接下来我们创建一个新的设计文件,命名为 ram_rw.v,代码如下:
module ram_rw(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
output ram_en , //ram使能信号
output ram_wea , //ram读写选择
output reg [4:0] ram_addr , //ram读写地址
output reg [7:0] ram_wr_data, //ram写数据
input [7:0] ram_rd_data //ram读数据
);
//reg define
reg [5:0] rw_cnt ; //读写控制计数器
//*****************************************************
//** main code
//*****************************************************
//控制RAM使能信号
assign ram_en = rst_n;
//rw_cnt计数范围在0~31,写入数据;32~63时,读出数据
assign ram_wea = (rw_cnt <= 6'd31 && ram_en == 1'b1) ? 1'b1 : 1'b0;
//读写控制计数器,计数器范围0~63
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
rw_cnt <= 1'b0;
else if(rw_cnt == 6'd63)
rw_cnt <= 1'b0;
else
rw_cnt <= rw_cnt + 1'b1;
end
//产生RAM写数据
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_wr_data <= 1'b0;
else if(rw_cnt <= 6'd31) //在计数器的0-31范围内,RAM写地址累加
ram_wr_data <= ram_wr_data + 1'b1;
else
ram_wr_data <= 1'b0 ;
end
//读写地址信号 范围:0~31
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_addr <= 1'b0;
else if(ram_addr == 5'd31)
ram_addr <= 1'b0;
else
ram_addr <= ram_addr + 1'b1;
end
ila_0 your_instance_name (
.clk(clk), // input wire clk
.probe0(ram_en), // input wire [0:0] probe0
.probe1(ram_wea), // input wire [0:0] probe1
.probe2(ram_addr), // input wire [0:0] probe2
.probe3(ram_wr_data),// input wire [4:0] probe3
.probe4(ram_rd_data) // input wire [7:0] probe4
);
endmodule
模块中定义了一个读写控制计数器(rw_cnt),当计数范围在 0~31 之间时,向 ram 中写入数据;当计数范围在 32~63 之间时,从 ram 中读出数据。
接下来我们设计一个 verilog 文件来实例化创建的 RAM IP 核以及 ram_rw 模块,文件名为 ip_ram.v,编写的 verilog 代码如下。
module ip_ram(
input sys_clk , //系统时钟
input sys_rst_n //系统复位,低电平有效
);
//wire define
wire ram_en ; //RAM使能
wire ram_wea ; //ram读写使能信号,高电平写入,低电平读出
wire [4:0] ram_addr ; //ram读写地址
wire [7:0] ram_wr_data ; //ram写数据
wire [7:0] ram_rd_data ; //ram读数据
//*****************************************************
//** main code
//*****************************************************
//ram读写模块
ram_rw u_ram_rw(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
//RAM
.ram_en (ram_en ),
.ram_wea (ram_wea ),
.ram_addr (ram_addr ),
.ram_wr_data (ram_wr_data ),
.ram_rd_data (ram_rd_data )
);
//ram ip核
blk_mem_gen_0 blk_mem_gen_0 (
.clka (sys_clk ), // input wire clka
.ena (ram_en ), // input wire ena
.wea (ram_wea ), // input wire [0 : 0] wea
.addra (ram_addr ), // input wire [4 : 0] addra
.dina (ram_wr_data ), // input wire [7 : 0] dina
.douta (ram_rd_data ) // output wire [7 : 0] douta
);
endmodule
`timescale 1ns / 1ps
module tb_ip_ram();
reg sys_clk;
reg sys_rst_n;
always #10 sys_clk = ~sys_clk;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
end
ip_ram u_ip_ram(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n )
);
endmodule
接下来就可以开始仿真了,仿真过程这里不再赘述,仿真波形图如下图所示。

上图为 RAM 的写操作仿真波形图,由上图可知,ram_wea 信号拉高,说明此时是对 ram 进行写操作。ram_wea 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1,我们总共向 ram 中写入 32 个数据。
RAM 读操作仿真波形图如下图所示:

set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]


ip_ram
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我想在Ruby的TCPServer中获取客户端的IP地址。以及(如果可能的话)MAC地址。例如,Ruby中的时间服务器,请参阅评论。tcpserver=TCPServer.new("",80)iftcpserverputs"Listening"loopdosocket=tcpserver.acceptifsocketThread.newdoputs"Connectedfrom"+#HERE!HowcanigettheIPAddressfromtheclient?socket.write(Time.now.to_s)socket.closeendendendend非常感谢!
Nginx在生产中的重要性通常基于它为慢速客户端提供服务的能力;在RESTfulAPI的设置中,它似乎是生产堆栈的一个不必要的层,尤其是Puma(不像广泛使用的unicorn可以处理nginx工作)。Pumacanallowmultipleslowclientstoconnectwithoutrequiringaworkertobeblockedontherequesttransaction.Becauseofthis,Pumahandlesslowclientsgracefully.HerokurecommendsPumaforuseinscenarioswhereyouexpect
一、RIPV2协议简介 RIP(RoutingInformationProtocol)路由协议是一种相对古老,在小型以及同介质网络中得到了广泛应用的一种路由协议。RIP采用距离向量算法,是一种距离向量协议。RIP-1是有类别路由协议(ClassfulRoutingProtocol),它只支持以广播方式发布协议报文。RIP-1的协议报文无法携带掩码信息,它只能识别A、B、C类这样的自然网段的路由,因此RIP-1不支持非连续子网(DiscontiguousSubnet)。RIP-2是一种无类别路由协议(ClasslessRoutingProtocol),支持路由标记,在路由策略中可根据路由标记对
一、离线方式1.1.下载ip2region.xdbGitHub项目地址:https://github.com/lionsoul2014/ip2region我们首先需要下载一个ip2region.xdb的文件下载地址:https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb打开后点击如图的Download图标即可下载。下载完成后,需要将该文件放到我们的项目中。ps:我是直接放到服务器的,因为放在项目的资源文件夹下,当我们调试的时候使用JavaSpring自带的工具去获取该文件的绝对路径时,没有任何问题,能够正
1、报文格式前几篇总结过较多网络层的知识,ARP,ip地址、子网掩码等等。这次总结一下IP数据报的报文格式IP数据报的格式能够说明IP协议都具有什么功能。在TCP/IP的标准中,各种数据格式常常以32位(4字节)为单位来描述的。wireshark中IP数据报文呈现一个IP数据报文由首部和数据两部分组成。首部的前一部分是固定长度,共20字节。是所有IP数据报文必须具有的。在首部的固定部分的后面是一些可选字段,其长度可变。2、首部个字段意义 1、版本占4位,指IP协议的版本。通信双方使用的IP协议的版本必须一致。目前广泛使用的IP协议版本号为4(即IPv4)。版本号为6(即IPv6) 2、首部
目录1.1访问Cisco路由器的方法1.1.1通过Console口访问路由器1.1.2通过Telnet访问路由器1.1.3终端访问服务器1.2终端访问服务器配置命令汇总1.1访问Cisco路由器的方法 路由器没有键盘和鼠标,要初始化路由器需要把计算机的串口和路由器的Console口进行连接。访问Cisco路由器的方法还有Telnet、WebBrowser和网络管理软件(如CiscoWorks)等,本节讨论前2种。1.1.1通过Console口访问路由器 计算机的串口和路由器的Console口是通过反转线(Rollover)进行连接的,反转线的一端接在路由器的Console口上,另一
我想做两件事:将IP地址输入转换为CIDR以下是一些示例输入:1.1.1.1192.168.*.*#=>192.168.0-255.0-255192.168.1.2-201.1.1-10.1-100检查给定的IP地址是否属于任何CIDR。这一定是一个非常快速的查询,因为它是我的网络应用程序中非常常见的查找。我正在考虑做这样的事情:defmatches?(request)valid=@ips.select{|cidr|cidr.contains?(request.remote_ip)}!valid.empty?end我认为将IP范围转换为CIDR将使查找速度比我们现在所做的更快,后者将I
您好,我将其视为一个面试问题,并认为这是一个有趣的问题,但我不确定答案。最好的方法是什么? 最佳答案 假设*nix:system("sortoutput_file")“排序”可以使用临时文件来处理大于内存的输入文件。如果需要,它有开关来调整主内存的数量和它将使用的临时文件的数量。如果不是*nix,或者面试官因为斜着回答皱眉,那我就编码一个外部mergesort.请参阅@psyho的回答以获得外部排序算法的良好总结。 关于ruby-使用Ruby作为脚本语言,使用4gbRAM的计算机对30g
我想记录用户的ip地址、referer和用户代理。在PHP中,我可以从以下变量中获取它们:$_SERVER['REMOTE_ADDR']$_SERVER['HTTP_REFERER']$_SERVER['HTTP_USER_AGENT']如何在ruby中获取它们? 最佳答案 PHP嵌入在网络服务器中。Ruby是一种通用语言:如果您需要Web服务器上下文,则必须自己安装。幸运的是,这很容易。最简单的入门方法之一是使用Sinatra。安装gem:geminstallsinatra然后创建myapp.rb:require'sinatr