学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。
学习视频:是根据野火FPGA视频教程——第二十三讲 到 第二十七讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3
这里进行常用的IP核简单的介绍,如果深度学习推荐数据:
《Xilinx系列FPGA芯片 IP核详解》
IP(Intellectual Property)即知识产权。美国 Dataquest 咨询公司将半导体产业的 IP 定义为“用于 ASIC 或 FPGA 中的预先设计好的电路功能模块”。简而言之,这里的 IP 即电路功能模块。IP 核在数字电路中常用于比较复杂的功能模块(如 FIFO、RAM、FIR 滤波器、SDRAM 控制器、PCIE 接口等)设计成参数可修改的模块,让其他用户可以直接调用这些模块。随着设计规模增大,复杂度提高,使用 IP 核可以提高开发效率,减少设计和调试时间,加速开发进程,降低开发成本,是业界的发展趋势。利用 IP 核设计电子系统,引用方便,修改基本元件的功能容易。具有复杂功能和商业价值的 IP 核一般具有知识产权,尽管 IP 核的市场活动还不规范,但是仍有许多集成电路设计公司从事 IP 核的设计、开发和营销工作。
分类依据:产品交付方式
硬件描述语言;可进行参数调整、复用性强;布局、布线灵活;设计周期短、设计投入少
完成了综合的功能块;可预布线特定信号或分配特定的布线资源
缺点:
IP核往往不能跨平台使用
IP核不透明,看不到内部核心代码
定制IP需额外收费
IP 核生成工具提供的 IP 核主要有以下几类:
1、数学运算模块,包括累加器、乘加器、乘累加器、计数器、加/减法器、实/复数乘法器、除法器、CORDIC 算法器、DSP48 宏和浮点数操作器。
2、存储器构造模块,包括块存储器和分布式存储器、先入先出存储器(FIFO)和移位寄存器。
3、DSP 功能,包括直接数字频率合成(DDS)编译器、数字上变频/下变频(DUC/DDC)编译器、有限冲激响应(FIR)滤波器、级联积分梳状(CIC)滤波器、离散傅里叶变换(DFT)和快速傅里叶变换(FFT)。
4、信道纠错码,包括 RS 码编码器和译码器、卷积码编码器、Viterbi 译码器、Turbo码编/译码器和低密度奇偶校验码(LDPC)编码器等。
5、网络应用,包括媒体访问控制器(MAC)、以太网物理编码子层/物理介质连接(PCS/PMA)、网络负载统计、以太网拓展连接单元接口(XAUI)、减少引脚使用的XAUI(RXAUI)、MAC 封装包和音/视频桥接(AVB)端点。
6、FPGA 结构属性,包括时钟向导、高速串行收发器(GTX/GTP)和系统监视向导。
7、连接器,包括标准总线接口(如 PCI/PCI-X、PCI Express、CAN)和数据接口(如以太网、RapidIO 等)。
8、调试和验证,包括逻辑调试内核(集成控制器核(ICON)、集成逻辑分析核(ILA)、虚拟输入/输出核(VIO)、Agilent 跟踪核(ATC2)、误比特率测试核(IBERT)和集成总线分析核(IBA)。
9、针对不同设计方法的特殊IP核,包括用工程导航工具进行逻辑设计的IP核、用Xilinx系统生成工具进行DSP算法设计的IP核,以及用Xilinx平台开发环境(XPS)或 PlanAhead进行嵌入式设计的IP核。
本章将重点介绍几个常用的 IP,如锁相环(PLL)、FIFO、RAM、ROM 等,详细说明各 IP 核的功能以及其使用方法,通过使用这些简单的 IP 核来掌握所有 IP 核的基本使用方法,起到抛砖引玉的效果。

一、PLL(锁相环)IP核 PLL(Phase Locked LoopP,即锁相环)是最常用的 IP 核之一,其性能强大,可以对输入到 FPGA 的时钟信号进行任意分频、倍频、相位调整、占空比调整,从而输出一个期望时钟,实际上,即使不想改变输入到 FPGA 时钟的任何参数,也常常会使用 PLL,因为经过 PLL 后的时钟在抖动(Jitter)方面的性能更好一些。Xilinx 中的 PLL 是模拟锁相环,和数字锁相环不同的是模拟锁相环的优点是输出的稳定度高、相位连续可调、延时连续可调;缺点是当温度过高或者电磁辐射过强时会失锁(普通环境下不考虑该问题)。
反馈系统,ref_clk:参考时钟,FD/PD:鉴频/相器。
1、参考时钟(ref_clk)通过鉴频(FD)鉴相器(PD)和需要比较的时钟频率进行比较,我们以频率调整为例,如果参考时钟频率等于需要比较的时钟频率则鉴频鉴相器输出为 0,如果参考时钟频率大于需要比较的时钟频率则鉴频鉴相器输出一个变大的成正比的值,如果参考时钟频率小于需要比较的时钟频率则鉴频鉴相器输出一个变小的正比的值。
2、鉴频鉴相器的输出连接到环路滤波器(LF)上,用于控制噪声的带宽,滤掉高频噪声,使之稳定在一个值,起到将带有噪声的波形变平滑的作用。如果鉴频鉴相器之前的波形抖动比较大,经过环路滤波器后抖动就会变小,趋近于信号的平均值。
3、经过环路滤波器的输出连接到压控振荡器(VCO)上,环路滤波器输出的电压可以控制 VCO 输出频率的大小,环路滤波器输出的电压越大 VCO 输出的频率越高,然后将这个频率信号连接到鉴频鉴相器作为需要比较的频率。

DIV(分频器),如果ref_clk为50MHz,假设DIV为1/2分频器,则最终分频输出为50MHz,那么之前的pll_out则为100MHz,实现了倍频的功能。

如果ref_clk为50MHz,假设DIV为1/5分频器,则最终DIV输出为10MHz,经过基本PLL结构,pll_out输出也为10MHz。

建立一个PLL工程,在工程中找到IP核目录,并搜索时钟clk,找到时钟IP核选项。

双击打开时钟IP核,配置界面

1、框中我们输入 IP 核的命名,后面实例化 IP 核的时候都是使用的该名字,这里所取的名字最好是和该 IP 核相关,因为本节我们主要讲解 PLL,所以给该 IP 核取名为 pll_ip。
2、 框中我们选中 PLL。
3 框中是输入 IP 核的输入时钟,由于我们开发板上的时钟晶振为 50MHz,所以这里我们输入 50。

设置完之后切换到“Output Clocks”页面。

1、框中是对输出时钟频率、相位以及占空比的设置。为了让大家能够看到 PLL 核每种参数的设置的效果,我们生成四个输出时钟,输出的设置分别为输入时钟的 2 倍频、即100MHz;输入时钟的 2 分频,即 25MHz;输入时钟像移 90°;输入时钟占空比为 20%。
2、框中是对复位管脚以及锁定管脚的设置。其中 reset 为复位管脚,用来对 PLL IP 进行复位,locked 为锁定管脚,用来检测 PLL IP 核是否已经锁定,当输出时钟稳定时该信号会输出高电平表示输出稳定。对于一般的应用而言,可以不用添加这两个管脚,这里我们只添加上“locked”生成锁定管脚,以便在仿真时能够体现 PLL 的工作特点。
设置完之后切换到“Port Renaming”页面。


生成IP核


首先使用的芯片、工具及版本相同,这里为了演示,先把上面建立好的IP核从Vivado中移除,然后进行添加演示。

打开IP核文件夹,添加IP核

实例化模板:从1、2文件中均可以找到实例化模板。

module pll(
input wire sys_clk,
output wire clk_mul_2,
output wire clk_div_2,
output wire clk_phase_90,
output wire clk_ducle_20,
output wire locked
);
//PLL IP核的调用
pll_ip pll_ip_inst
(
// Clock out ports
.clk_100m(clk_mul_2), // output clk_100m
.clk_25m(clk_div_2), // output clk_25m
.clk_p_90(clk_phase_90), // output clk_p_90
.clk_d_20(clk_ducle_20), // output clk_d_20
// Status and control signals
.locked(locked), // output locked
// Clock in ports
.pll_clk(sys_clk)); // input pll_clk
endmodule
`timescale 1ns / 1ns
//
// Company: 追逐者-桥的小作坊
// Create Date: 2022/05/26 17:50:56
// Design Name: PLL IP核
// Module Name: tb_pll
module tb_pll();
reg sys_clk;
wire clk_mul_2;
wire clk_div_2;
wire clk_phase_90;
wire clk_ducle_20;
wire locked;
initial sys_clk = 1'b1;
always #10 sys_clk = ~sys_clk;
pll pll_inst(
. sys_clk (sys_clk ),
. clk_mul_2 (clk_mul_2 ),
. clk_div_2 (clk_div_2 ),
. clk_phase_90(clk_phase_90),
. clk_ducle_20(clk_ducle_20),
. locked (locked )
);
endmodule

ROM 是只读存储器(Read-Only Memory)的简称,是一种只能读出事先所存数据的固态半导体存储器。其特性是一旦储存资料就无法再将之改变或删除,且资料不会因为电源关闭而消失。而事 实上在 FPGA 中通过 IP 核生成的 ROM 或 RAM(RAM 将在下一节为大家讲解)调用的都是FPGA 内部的 RAM 资源,掉电内容都会丢失(这也很容易解释,FPGA 芯片内部本来就没有掉电非易失存储器单元)。用 IP 核生成的 ROM 模块只是提前添加了数据文件(.coe 格式)(.mf/.nex格式),在 FPGA 运行时通过数据文件给 ROM 模块初始化,才使得 ROM 模块像个“真正”的掉电非易失存储器;也正是这个原因,ROM 模块的内容必须提前在数据文件中写死,无法在电路中修改。
最简单的使用有效时钟CLKA、有效地址ADDRA和有效使能EA,就可以输出DOUTA


ROM 作为只读存储器,在进行 IP 核设置时需要指定初始化文件,即写入存储器中的数据,数据要以规定的格式才能正确写入 ROM,这种格式就是 coe 文件。coe 是 Vivado 规定的一种文件格式,文件格式示意图,具体见图 25-24。

比较复杂时可以使用python和matlab自动生成文件。
打开IP核目录并搜索找到ROM IP核:RAM & ROM 核“Block Memory Generator”双击打开配置。

1、框中我们输入 IP 核的命名。
2、类型,普通和接口形式。
3、框中选择存储器类型,可供选择的类型有:Single Port RAM(单端口 RAM)、Simple Dual Port RAM(简单双口 RAM)、True Dual Port RAM(真双口 RAM)、Singl Port ROM(单端口 ROM)、Doul Port ROM(双端口 ROM)。这里我们选择“Single Port Rom”单端口 ROM。
4、框在 Algorithm 一栏中可选择用于实现内存的算法,其中 Minimum Area 为最小面积算法;Low Power 为低功耗算法;Fixed Primitives 为固定单元算法。这里我们按默认选择Minimum Area 即可。
5、校验选项,只有RAM中才会使用到。
6、设置使能位数,也是只有在RAM中才会用到。


框中选项是加载数据文件,即我们前面讲到的 ROM 初始化文件,由于 ROM 是只读存储器,所以我们必须添加 ROM 初始化文件才行。勾选上“Load Init File”点击 Browse 进行添加.coe 初始化文件,文件格式我们之前已经讲解,大家可根据自己想存入的数据进行生成该文件。该页面其余按默认设置即可。

在“Summary”页面,从“IP Symbol”窗口可以看到我们最终创建的 ROM 核的端口信号。点击“OK”完成 ROM 的创建设置。




该窗口是对端口 B 的设置,这也是双端口 ROM 与单端口 ROM 最大的区别,多了一组端口。这里我们只需对 B 端口的数据位宽设置即可,数据深度会根据端口 A 的设置自动设置。例如我们端口 A 设置的数据位宽为 8bit,深度为 256;而我们 B 端口设置的数据位宽为 16bit,则其深度即为 128,其数据总量是一样的。其余设置与端口 A 设置一样即可。


与之前的PLL类似调用类似,打开IP Sources找到.veo文件,内有实例化模型之间复制
module rom(
input wire sys_clk,
input wire [7:0] addra,
output wire [7:0] douta
);
rom_8x256 rom_ip (
.clka(sys_clk), // input wire clka
.addra(addra), // input wire [7 : 0] addra
.douta(douta) // output wire [7 : 0] douta
);
endmodule
`timescale 1ns / 1ns
//
// Company: 追逐者-桥的小作坊
// Create Date: 2022/05/27 15:05:26
// Design Name: ROM IP核
// Module Name: tb_rom_ip
//
module tb_rom_ip();
reg sys_clk;
reg sys_rst_n;
reg [7:0] addra;
wire [7:0] douta; //将数据引出因此设置为网线型
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~ sys_clk;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
addra <= 8'd0;
else if(addra == 8'd255)
addra <= 8'd0;
else
addra <= addra + 1'b1;
rom rom_inst(
.sys_clk(sys_clk),
.addra (addra ),
.douta (douta )
);
endmodule
有波形图可以看出可以通过输入相应的地址返回相应的数据,但是两者的时钟差2个时钟。

下图框中是否在输出增加一个寄存器,如果加入寄存器会在增加一个时钟周期,不增加仅滞后一个时钟周期。

将其取消选取时,可以看出只延迟了1个时钟周期:

RAM 是随机存取存储器(Random Access Memory)的简称,是一个易失性存储器。 RAM 工作时可以随时从任何一个指定的地址写入或读出数据,同时我们还能修改其存储的数据,即写入新的数据,这是 ROM 所并不具备的功能。在 FPGA 中这也是其与 ROM 的最大区别。ROM 是只读存储器,而 RAM 是可写可读存储器,在我们 FPGA 中使用这两个存储器主要也是要区分这一点,因为这两个存储器使用的都是我们 FPGA 内部的 RAM 资源,不同的是 ROM 是只用到了 RAM 资源的读数据端口。



单端口配置

操作方式:
Write First:读写在同一始终下,先执行写操作,读出的是写入的值。
Read First:读写在同一始终下,先执行读操作,读出的是之前写入的值。
NO:读和写不能在同一始终下进行

后面两栏基本不用配置,直接生成IP核即可。


module ram(
input wire sys_clk,
input wire [7:0] addra,
input wire [7:0] dina,
input wire wea, //写使能
output wire [7:0] douta
);
s_ram_gen_8x256 s_ram_gen_8x256_inst (
.clka(sys_clk), // input wire clka
.wea(wea), // input wire [0 : 0] wea
.addra(addra), // input wire [7 : 0] addra
.dina(dina), // input wire [7 : 0] dina
.douta(douta) // output wire [7 : 0] douta
);
endmodule
FIFO(First In First Out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。与 ROM 或 RAM 的按地址读写方式不同,FIFO 的读写遵循“先进先出”的原则,即数据按顺序写入 FIFO,先被写入的数据同样在读取的时候先被读出,所以 FIFO存储器没有地址线。FIFO 有一个写端口和一个读端口外部无需使用者控制地址,使用方便。
FIFO 存储器主要是作为缓存,应用在同步时钟系统和异步时钟系统中,在很多的设计中都会使用,后面实例中如:多比特数据做跨时钟域的转换、前后带宽不同步等都用到了FIFO。FIFO 根据读写时钟是否相同,分为 SCFIFO(同步 FIFO)和 DCFIFO(异步FIFO),SCFIFO 的读写为同一时钟,应用在同步时钟系统中;DCFIFO 的读写时钟不同,应用在异步时钟系统中。
数据的产生模块与数据使用模块不对应时就会使用到FIFO,如两者的时钟频率不同无法在同一时钟下进行传输(多比特数据做跨时钟域的转换) ;两者的数据带宽不同下传输(前后带宽不同步)。
在IP Catalog中搜索FIFO,并选择fifo核“FIFO Generator”,双击打开






RTL 代码顶层的输入信号有:50MHz 的写时钟 wr_clk、输入 256 个 8bit 的数据 pi_data(值为十进制 0~255)和伴随该输入数据有效的标志信号 pi_flag、25MHz 的读时钟rd_clk、FIFO 的写请求信号 rd_en。这些输入信号需要在 Testbench 中产生激励。
RTL 代码顶层的输出信号有:FIFO 空标志信号 empty、FIFO 满标志信号 full、同步于wr_clk 指示 FIFO 中存在数据个数的信号 wr_data_count、从 FIFO 中读取的数据 po_data、同步于 rd_clk 指示 FIFO 中存在数据个数的信号 rd_data_count。这些信号也是我们需要通过仿真 DCFIFO IP 核主要观察的信号,这些信号通过 Testbench 中给输入信号激励后产生输出。
module scfifo(
input wire sys_clk,
input wire sys_rst_n,
input wire [7:0] pi_data, //写数据
input wire pi_flag, //写请求信号
input wire rd_en, //FIFO读请求信号
output wire [7:0] po_data, //读数据
output wire full, //FIFO满标志,高有效
output wire empty, //FIFO空标志,高有效
output wire [7:0] data_count //FIFO中存在的数据个数
);
scfifo_8x256 scfifo_8x256_inst (
.clk(sys_clk), // input wire clk
.srst(~sys_rst_n), // input wire srst
.din(pi_data), // input wire [7 : 0] din
.wr_en(pi_flag), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(po_data), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.data_count(data_count) // output wire [7 : 0] data_count
);
endmodule
`timescale 1ns / 1ns
module tb_scfifo();
reg sys_clk;
reg sys_rst_n;
reg [7:0] pi_data;
reg pi_flag;
reg rd_en;
reg [1:0] cnt_baud;
wire [7:0] po_data;
wire full;
wire empty;
wire [7:0] data_count;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//计数器,产生数据间的间隔
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;
//pi_flag:输入标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_flag <= 1'b0;
else if((cnt_baud == 2'd0) && (rd_en == 1'b0))
pi_flag <= 1'b1;
else
pi_flag <= 1'b0;
//pi_data:数据写入
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'b0;
else if((pi_data == 8'd255) && (pi_flag == 1'b1))
pi_data <= 8'b0;
else if(pi_flag == 1'b1)
pi_data <= pi_data + 1'b1;
//rd_en:读请求信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if(full == 1'b1)
rd_en <= 1'b1;
else if(empty == 1'b1)
rd_en <= 1'b0;
scfifo scfifo_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_data (pi_data ), //写数据
.pi_flag (pi_flag ), //写请求信号
.rd_en (rd_en ), //FIFO读请求信号
.po_data (po_data ), //读数据
.full (full ), //FIFO满标志,高有效
.empty (empty ), //FIFO空标志,高有效
.data_count(data_count) //FIFO中存在的数据个数
);
endmodule
虽然使用的是同步时钟,但是写的时钟间隔做了改变,而读的时钟依然使用的是系统时钟。

RTL 代码顶层的输入信号有:50MHz 的写时钟 wr_clk、输入 256 个 8bit 的数据 pi_data(值为十进制 0~255)和伴随该输入数据有效的标志信号 pi_flag、25MHz 的读时钟 rd_clk、FIFO 的写请求信号 rd_en。这些输入信号需要在 Testbench 中产生激励。
RTL 代码顶层的输出信号有:FIFO 空标志信号 empty、FIFO 满标志信号 full、同步于wr_clk 指示 FIFO 中存在数据个数的信号 wr_data_count、从 FIFO 中读取的数据 po_data、同步于 rd_clk 指示 FIFO 中存在数据个数的信号 rd_data_count。这些信号也是我们需要通过仿真 DCFIFO IP 核主要观察的信号,这些信号通过 Testbench 中给输入信号激励后产生输出。
module dcfifo(
input wire wr_clk, rd_clk,
input wire [7:0] din,
input wire wr_en, rd_en,
output wire full, empty,
output wire [15:0] dout,
output wire [6:0] rd_data_count,
output wire [7:0] wr_data_count
);
dcfifo_gen dcfifo_gen_inst (
.wr_clk(wr_clk), // input wire wr_clk
.rd_clk(rd_clk), // input wire rd_clk
.din(din), // input wire [7 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [15 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.rd_data_count(rd_data_count), // output wire [6 : 0] rd_data_count
.wr_data_count(wr_data_count) // output wire [7 : 0] wr_data_count
);
endmodule
`timescale 1ns / 1ns
module tb_dcfifo();
reg wr_clk;
reg rd_clk;
reg [7:0] din;
reg wr_en;
reg rd_en;
reg sys_rst_n;
reg [1:0] cnt_baud;
reg full_reg0;
reg full_reg1;
wire empty;
wire full;
wire [15:0] dout;
wire [6:0] rd_data_count;
wire [7:0] wr_data_count;
initial begin
wr_clk = 1'b1;
rd_clk = 1'b1;
sys_rst_n <= 1'b0;
#100;
sys_rst_n <= 1'b1;
#100000
sys_rst_n <= 1'b0;
end
always #10 wr_clk = ~wr_clk;
always #20 rd_clk = ~rd_clk;
//计数器,产生数据间的间隔
always@(posedge wr_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;
//wr_en:输入标志信号
always@(posedge wr_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en <= 1'b0;
else if((cnt_baud == 2'd0) && (rd_en == 1'b0))
wr_en <= 1'b1;
else
wr_en <= 1'b0;
//din:数据写入
always@(posedge wr_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
din <= 8'b0;
else if((din == 8'd255) && (wr_en == 1'b1))
din <= 8'b0;
else if(wr_en == 1'b1)
din <= din + 1'b1;
//将同步于rd_clk时钟的写满标志信号full在rd_clk时钟下打两拍
always@(posedge rd_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
full_reg0 <= 1'b0;
full_reg1 <= 1'b0;
end
else
begin
full_reg0 <= full;
full_reg1 <= full_reg0;
end
//rd_en:读请求信号
always@(posedge rd_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if(full_reg1 == 1'b1)
rd_en <= 1'b1;
else if(empty == 1'b1)
rd_en <= 1'b0;
dcfifo dcfifo_inst(
.wr_clk (wr_clk ),
.rd_clk (rd_clk ),
.din (din ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.full (full ),
.empty (empty ),
.dout (dout ),
.rd_data_count(rd_data_count),
.wr_data_count(wr_data_count)
);
endmodule

我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我有一个存储主机名的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
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc