Verilog入门精简教程_verilog教程-程序员宅基地

技术标签: ZYNQ  

Verilog入门

1 关键字

1.1 module

module()

endmodule

代表一个模块,我们的代码写在这个两个关键字中间

1.2 input output

input关键词,模块的输入信号,比如input Clk,Clk是外面关键输入的时钟信号;

output关键词,模块的输出信号,比如output[3:0]Led; 这个地方正好是一组输出信号。其中[3:0]表示0~3共4路信号。

inout模块输入输出双向信号。数据总线的通信中,这种信号被广泛应用;

wire关键词,线信号。例如:wire C1_Clk; 其中C1_Clk就是wire类型的信号;

线信号,三态类型,我们一般常用的线信号类型有input,output,inout,wire;

reg关键词,寄存器。和线信号不同,它可以在always中被赋值,经常用于时序逻辑中。比如reg[3:0]Led;表示了一组寄存器。

1.3 always

always@()括号里面是敏感信号。

always@(posedge Clk)敏感信号是posedge Clk含义是在上升沿的时候有效。

敏感信号还可以negedge Clk含义是下降沿的时候有效,

这种形式一般时序逻辑都会用到。

还可以是这个一符号,如果是一个则表示一直是敏感的,一般用于组合逻辑。

1.4 assign

assign用来给output,inout以及wire这些类型进行连线。

assign相当于一条连线,将表达式右边的电路直接通过wire(线)连接到左边,

左边信号必须是wire型(output和inout属于wire型)。

当右边变化了左边立马变化,方便用来描述简单的组合逻辑。

示例:

wire a, b, y;

assign y = a & b;

1.5 if…else…

这些语句含义上都和高级语言一样:

if(…)

begin

end

if(…)

begin

end

else begin

end

if(…)

begin

endelse

if(…)

begin

end

case(…)

endcase

1.6 case…endcase

case…

endcase

作用域用于状态机的编写

case(s)

1:

begin …end

2:

begin …end

default:

begin…end

endcase

1.7 begin…and

begin

end

作用域范围,类似于C的大括号。

用法举例:

always@(posedge clk)

begin

end

1.8 parameter

parameter定义一个符号a为常数(十进制180找个常量的定义等效方式):

parameter a = 180;//十进制,默认分配长度32bit(编译器默认)

parameter a = 8’d180;//十进制

parameter a = 8’HB4; //十六进制

parameter a = 8’b1011_0100; //二进制

1.9 include&define

Include 和define都是预处理命令,用于常量阐述的定义

//--------------------------------include file1.v

//--------------------------------define X = 1;

//--------------------------------deine Y;

ifdef YZ=1;

else Z=0;

endif//--------------------------------

有的时候我们一些公共的宏参数,我们可以放在一个文件中,比如这个文件名字为xx.v那么我们可以`include xx.v就可以包含找个文件中定义的一些宏参数。我还是来详细说明下吧!

话说Verilog 的`include和C语言的include用法是一样一样的,要说区别可能就在于那个点吧。

include一般就是包含一个文件,对于Verilog这个文件里的内容无非是一些参数定义,所以这里再提几个关键字:ifdefdefine `endif(他们都带个点,呵呵)。

他们联合起来使用,确实能让你的程序多样化,就拿VGA程序说事吧。

首先,你可以新建一个.v文件(可以直接新建一个TXT,让后将后缀换成.v)其实这个后缀没所谓,.v也是可以的,我觉得,写成.v更能体现出这个文件的意义。

其次`include “xxx.v” 这个路径也有一点讲究,xxx.v作为引用lcd_para.v的文件它和lcd_para.v在同一文件夹下才能怎么写,就是相对路径一说了。也就是以xxx.v为当前路径去引索lcd_para.v文件的位子。所以如果他们不再一个文件夹那么请写出更详细(正确)的路径。顺便说一句,lcd_para.v添不添加到工程是无所谓的,只要路径

对了即可,当然我还是建议添加到工程,且和.v文件放在同一文件夹下,以方便观察和管理。

1.10 符号部分

“;”分号用于每一句代码的结束,以表示结束,和C语言一样。

“:”冒号,用在数组,和条件运算符以及case语句结构中。

“<=”赋值符号,非阻塞赋值,在一个always模块中,所有语句一起更新。它也可以表示小于等于,具体是什么含义编译环境根据当前编程环境判断,如果“<=”是用在一个if判断里如:if(a <= 10);当然就表示小于等于了。

“=”阻塞赋值,或者给信号赋值,如果在always模块中,这条语句被立刻执行。阻塞赋值和非阻塞赋值将再后面详细举例说明。

“+,-,*,/,% ”是加、减、乘、除运算符号,这些使用和C语言基本是一样的,当你用到这些符号时,编译后会自动生成或者消耗FPGA原有的加法器或是乘法器等。其中符号/,%会消耗大量的逻辑,谨慎使用。

“<”小于,比如A

“<=”小于等于,比如A<=B含义就是A和B比较,如果A小于等于B就是TURE,否则为FALSE。

“>”大于,比如A>B含义就是A和B比较,如果A大于B就是TURE,否则为FALSE。

“>=”大于等于,比如A>=B含义就是A和B比较,如果大于等于B就是TURE,否则为FALSE。

”等于等于,比如AB含义就是A和B比较,如果A等于B就是TURE,否则为FALSE。

“!=”不等于,A!=B含义是A和B比较,如果A不等于B就是TURE,否则为FALSE.

“>>”右移运算符,比如A>>2表示把A右移2位。

“<<”左移运算符,比如A<<2表示把A左移2位。

”按位取反运算符,比如A=8’b1111_0000;则A的值为8’b0000_1111;

“&”按位于与,比如A=8’b1111_0000;B=8’b1010_1111;则A&B结果为8’b1010_0000;

”异或运算符,比如A=8’b1111_0000;B=8’b1010_1111;则AB结果为8’b0101_1111;

“&&”逻辑与,比如A1,B2;则A&&B结果为TRUE;如果A1,B0,则A&&B结果为FALSE,一般用于条件判断。

A = B ? C : D是一个条件运算符,含义是如果B为TRUE则把C连线A,否则把D连线A。B通常是个条件判断,用小括弧括起:

assign C1_Clk = (C1==25’d24999999) ? 1 : 0 ;

C1_Clk,是一个wire类型的信号,当C1==25’d24999999时候,连线到1,否则连线到0.

“{}”在Verilog中表示拼接符,{a,b}这个的含义是将括号内的数按位并在一起,比如:{1001,1110}表示的是10011110。

拼接是Verilog相对于其他语言的一大优势。

2 Verilog中数值表示的方式

如果我们要表示一个十进制是180的数值,在Verilog中的表示方法如下:

二进制:8’b1011_0100; //其中“_”是为了容易观察位数,可有可无。

十进制:8’d180;

16进制:8’HB4;

3 阻塞赋值和非阻塞赋值详解

说到阻塞赋值和非阻塞赋值,是很多初学者很迷惑的地方。

原因是C语言没有可以类比的东西。

学习FPGA和单片机最大的区别在于,学FPGA时,你必须时刻都有着时钟的概念。不像单片机时钟相关性比较差,FPGA你必须却把握每一个时钟。

首先来说说非阻塞赋值,这个在时序逻辑中随处可见:

reg A;

reg B;

always @(posedge clk)
    begin
       A <= 1'b1;
       B <= 1'b1;/***或者**B <= 1'b1;A <= 1'b1;*********/
end 

这段程序里,A和B是同时被赋值的,具体是说在时钟的上升沿来的时刻,A和B同时被置1。调换A和B的上下顺序,将得到相同的结果。

接着看另外一段程序:

reg A;
reg B;
always @(posedge clk)
    begin
        A <= 1'b1;
        end
always @(posedge clk)
    begin
        B <= 1'b1;
end 

这段程序,与第一段程序也是完全等价的,A和B在同一时刻被赋值。两段程序综合出的逻辑也是完全相同的。这就是非阻塞赋值的特点,体现了FPGA的并行性!

接着来看阻塞赋值,它少了一个非,表示会阻塞住,那么体会下这个阻塞:

always @(posedge clk)
    begin
        A  = 1'b1;
        B  <= 1'b1;
    end

看到,上面这个程序是阻塞和非阻塞的混合使用,一般教材是极力反对这种写法的。其实只要你理解了,有的时候这种用法还能帮上大忙。只不过,不理解的话乱用会导致时序违规。

回到正题,我们这么写是为了更好的理解阻塞赋值:当时钟上升沿来临的时刻,首先A会被置1,然后B寄存器再置1。区别就是A和B不再同时置1。A要比B提前零点几纳秒。这样就出现了先后顺序。这个过程还是在一个时钟内完成的,但是数据到达B寄存器相比上面两段程序晚了那么零点几纳秒!

当我们的时钟跑的比较慢的时候,比如50M,一个周期有20ns,那么这么短暂的延时基本可以忽略不计,但是随着设计的复杂,以及时钟速度的提高,这样的语句就要小心。

假设,我们要计算AB求和再除以2的结果。先用非阻塞方法去实现,由于AB求和再除以2是两个步骤,而非阻塞所以的事情都在一个时钟完成,所以这里我们用状态机,将两个步骤分配到两个时钟里去完成:

module unblock(
    input clk_i,
    input rst_n_i,
    output reg [4:0]result_o 
); 
    reg [3:0]A;  
    reg [3:0]B; 
    reg [4:0]C;  
    reg i; 
    always @(posedge clk_i ) 
        if(!rst_n_i) 
            begin 
                #2  A <= 4'd4;  
                B <= 4'd12;  
                C <= 5'd0;  
                result_o = 5'd0; 
            end  
    else 
        begin 
        #2   
            C <= A + B;  
            result_o <= (C >> 1); 
            end
endmodule

第一个时钟上升沿来临时,完成C <= A + B;

第二个时钟来临时完成result <= (C >> 1);

求出结果,这个过程耗费两个时钟。(不考虑复位消耗的时钟)

再来,用添加阻塞的方式实现:

module block(
    input clk_i,
    input rst_n_i,
    output reg [4:0]result_o 
);  
    reg [3:0]A;  
    reg [3:0]B; 
    reg [4:0]C; 
    always @(posedge clk_i) 
        if(!rst_n_i)  
            begin 
                #2 
                A = 4'd4; 
                #0.2 
                B = 4'd12; 
                #0.2 
                C = 5'd0;  
                #0.2 
                result_o = 5'd0; 
            end 
    else 
        begin  
            #2  
            C = A + B;  
            #0.2 
            result_o = (C >> 1);  
        end 
endmodule

4 Verilog HDL代码规范

  1. 接口时序设计规范

模块和模块之间的通过模块的接口实现关联,因此规范的时序设计,对于程序设计的过程,以及程序的维护,团队之间的沟通都是非常必要的。

img

命名规则

1、顶层文件

对象+功能+top

比如:video_oneline_top

2、逻辑控制文件

介于顶层和驱动层文件之间

对象+ctr

比如:ddr_ctr.v

3、驱动程序命名

对象+功能+dri

比如:lcd_dri.v、uart_rxd_dri.v

4、参数文件命名

对象+para

比如:lcd_para.v

5、模块接口命名:文件名+u

比如lcd_dir lcd_dir_u(…)

6、模块接口命名:特征名+文件名+u

比如 mcb_read c3_mcb_read_u

7、程序注释说明

8、端口注释

input Video_vs_i,//输入场同步入

9、信号命名

命名总体规则:对象+功能(+极性)+特性

10、时钟信号

对象+功能+特性

比如:phy_txclk_i、sys_50mhz_i

11、复位信号

对象+功能+极性+特性

比如:phy_rst_n_i、sys_rst_n_i

12、延迟信号

对象+功能+特性1+特征2

比如:fram_sync_i_r0、fram_sync_i_r1

13、特定功能计数器

对象+cnt

比如:line_cnt、div_cnt0、div_cnt1

功能+cnt

比如:wr_cnt、rd_cnt

对象+功能+cnt

比如:fifo_wr_cnt、mcb_wr_cnt、mem_wr_cnt

对象+对象+cnt

比如:video_line_cnt、video_fram_cnt

14、一般计数器

cnt+序号

用于不容易混淆的计数

比如:cnt0、cnt1、cnt2

15、 时序同步信号

对象+功能+特性

比如:line_sync_i、fram_sync_i

16、 使能信号

功能+en

比如:wr_en、rd_en

对象+功能+en

比如:fifo_wr_en、mcb_wr_en

5 状态机

状态机的状态转移图,通常也可根据输入和内部条件画出。

一般来说,状态机的设计包含下列设计步骤:

• 根据需求和设计原则,确定是Moore型还是Mealy型状态机;

• 分析状态机的所有状态,对每一状态选择合适的编码方式,进行编码;

• 根据状态转移关系和输出绘出状态转移图;

• 构建合适的状态机结构,对状态机进行硬件描述。

状态机的描述通常有三种方法,称为一段式状态机,二段式状态机和三段式状态机。

一段式状态机是应该避免使用的,该写法仅仅适用于非常简单的状态机设计,不符合组合逻辑与时序逻辑分开的原则,整个结构代码也不清晰,不利用维护和修改。

两段式状态机采用两个always模块实现状态机的功能,其中一个always采用同步时序逻辑描述状态转移,另一个always采用组合逻辑来判断状态条件转移。两段式状态机是推荐的状态机设计方法。

三段式状态机在第一个always模块采用同步时序逻辑方式描述状态转移,第二个always模块采用组合逻辑方式描述状态转移规律,第三个always描述电路的输出。通常让输出信号经过寄存器缓存之后再输出,消除电路毛刺。这种状态机也是比较推崇的,主要是由于维护方便,组合逻辑与时序逻辑完全独立。

状态机的描述通常包含以下四部分:

1)利用参数定义语句parameter描述状态机各个状态名称,即状态编码。状态编码通常有很多方法包含自然二进制编码,One-hot编码,格雷编码码等;

2)用时序的always块描述状态触发器实现状态存储;

3)使用敏感表和case语句(也采用if-else等价语句)描述状态转换逻辑;

4)描述状态机的输出逻辑。

6 仿真测试

编写Testbench测试文件的过程如下:

• 产生模拟激励(波形);

• 将产生的激励加入到被测试模块中并观察其响应;

• 将输出响应与期望值相比较。

一个完整的测试文件其结构为:

module Test_bench();//通常无输入无输出

信号或变量声明定义

逻辑设计中输入对应reg型

逻辑设计中输出对应wire型

使用initial或always语句产生激励

例化待测试模块监控和比较输出响应

endmodule

时钟激励设计

/*----------------------------------------------------------------

时钟激励产生方法一:50%占空比时钟

----------------------------------------------------------------*/

parameter ClockPeriod=10;

initial

begin

clk_i=0;

forever

#(ClockPeriod/2) clk_i=~clk_i;

end

/*----------------------------------------------------------------

时钟激励产生方法二:50%占空比时钟

----------------------------------------------------------------*/

initial

begin

clk_i=0;

always #(ClockPeriod/2) clk_i=~clk_i;

end



/*----------------------------------------------------------------

时钟激励产生方法四:产生固定数量的时钟脉冲

----------------------------------------------------------------*/

initial

begin

clk_i=0;

repeat(6)

#(ClockPeriod/2) clk_i=~clk_i;

end



/*----------------------------------------------------------------

时钟激励产生方法五:产生非占空比为50%的时钟

----------------------------------------------------------------*/

initial

begin

clk_i=0;

forever

begin

#((ClockPeriod/2)-2) 
    clk_i=0;

#((ClockPeriod/2)+2)
    clk_i=1;

end

end

复位信号设计

/*----------------------------------------------------------------

复位信号产生方法一:异步复位

----------------------------------------------------------------*/

initial

begin

rst_n_i=1;

#100;

rst_n_i=0;

#100;

rst_n_i=1;

end



/*----------------------------------------------------------------

复位信号产生方法二:同步复位

----------------------------------------------------------------*/

initial

begin

rst_n_i=1;

@(negedge clk_i)

rst_n_i=0;

#100; //固定时间复位

repeat(10) @(negedge clk_i); //固定周期数复位

@(negedge clk_i)

rst_n_i=1;

end



/*----------------------------------------------------------------

复位信号产生方法三:复位任务封装

----------------------------------------------------------------*/

task reset;

    input [31:0] reset_time; //复位时间可调,输入复位时间

RST_ING = 0; //复位方式可调,低电平或高电平

begin

rst_n = RST_ING; //复位中

#reset_time; //复位时间

rst_n_i=~RST_ING; //撤销复位,复位结束

end

endtask

双向信号设计

/*----------------------------------------------------------------

双向信号描述一:inout在testbench中定义为wire型变量

----------------------------------------------------------------*/

//为双向端口设置中间变量inout_reg作为inout的输出寄存,其中inout变

//量定义为wire型,使用输出使能控制传输方向

//inout bir_port;

wire bir_port;

reg bir_port_reg;

reg bi_port_oe;

assign bi_port=bi_port_oe ? bir_port_reg : 1'bz;



/*----------------------------------------------------------------

双向信号描述二:强制force

----------------------------------------------------------------*/

//当双向端口作为输出口时,不需要对其进行初始化,而只需开通三态门

//当双向端口作为输入时,只需要对其初始化并关闭三态门,初始化赋值需

//使用wire型数据,通过force命令来对双向端口进行输入赋值

//assign dinout=(!en) din :16'hz; 完成双向赋值

initial

begin

force dinout=20;

#200

force dinout=dinout-1;

end


特殊信号设计

/*----------------------------------------------------------------

特殊激励信号产生描述一:输入信号任务封装

----------------------------------------------------------------*/

task i_data;

input [7:0] dut_data;

begin

@(posedge data_en); send_data=0;

@(posedge data_en); send_data=dut_data[0];

@(posedge data_en); send_data=dut_data[1];

@(posedge data_en); send_data=dut_data[2];

@(posedge data_en); send_data=dut_data[3];

@(posedge data_en); send_data=dut_data[4];

@(posedge data_en); send_data=dut_data[5];

@(posedge data_en); send_data=dut_data[6];

@(posedge data_en); send_data=dut_data[7];

@(posedge data_en); send_data=1;

#100;

end

endtask

//调用方法:i_data(8'hXX);

/*----------------------------------------------------------------

特殊激励信号产生描述二:多输入信号任务封装

----------------------------------------------------------------*/

task more_input;

input [7:0] a;

input [7:0] b;

input [31:0] times;

output [8:0] c;

begin

repeat(times) //等待times个时钟上升沿

@(posedge clk_i)

c=a+b; //时钟上升沿a,b相加

end

endtask

//调用方法:more_input(x,y,t,z);  //按声明顺序

/*----------------------------------------------------------------

特殊激励信号产生描述三:输入信号产生,一次SRAM写信号产生

----------------------------------------------------------------*/

initial

begin

cs_n=1; //片选无效

wr_n=1; //写使能无效

rd_n=1; //读使能无效

addr=8'hxx; //地址无效

data=8'hzz; //数据无效

#100;

cs_n=0; //片选有效

wr_n=0; //写使能有效

addr=8'hF1; //写入地址

data=8'h2C; //写入数据

#100;

cs_n=1;

wr_n=1;

#10;

addr=8'hxx;

data=8'hzz;

end



/*----------------------------------------------------------------

Testbench中@与wait

----------------------------------------------------------------*/

//@使用沿触发

//wait语句都是使用电平触发

initial

begin

start=1'b1;

wait(en=1'b1);

#10;

start=1'b0;

end

仿真控制语句及系统任务描述

/*----------------------------------------------------------------

仿真控制语句及系统任务描述

----------------------------------------------------------------*/

$stop     //停止运行仿真,modelsim中可继续仿真

$stop(n) //带参数系统任务,根据参数0,1或2不同,输出仿真信息

$finish   //结束运行仿真,不可继续仿真

$finish(n)  //带参数系统任务,根据参数0,1或2不同,输出仿真信息

//0:不输出任何信息

//1:输出当前仿真时刻和位置

//2:输出当前仿真时刻、位置和仿真过程中用到的memory以及CPU时间的统计

$random //产生随机数

$random % n //产生范围-n到n之间的随机数

{$random} % n //产生范围0到n之间的随机数



/*----------------------------------------------------------------

仿真终端显示描述

----------------------------------------------------------------*/

$monitor //仿真打印输出,大印出仿真过程中的变量,使其终端显示

/*

$monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);

*/

$display //终端打印字符串,显示仿真结果等

/*

$display(” Simulation start ! ");

$display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z);



*/

$time //返回64位整型时间

$stime //返回32位整型时间

$realtime //实行实型模拟时间

/*----------------------------------------------------------------

文本输入方式:$readmemb/$readmemh

----------------------------------------------------------------*/

//激励具有复杂的数据结构

//verilog提供了读入文本的系统函数

$readmemb/$readmemh("<数据文件名>",<存储器名>);

$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>);

$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);

$readmemb:/*读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数

数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。*/

$readmemh:/*读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数

数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字。*/

      /*当地址出现在数据文件中,格式为@hh...h,地址与数字之间不允许空白位置,

可出现多个地址*/

module

reg [7:0] memory[0:3];//声明8个8位存储单元

integer i;

initial

begin

$readmemh("mem.dat",memory);//读取系统文件到存储器中的给定地址

//显示此时存储器内容

for(i=0;i<4;i=i+1)

$display("Memory[%d]=%h",i,memory[i]);

end

endmodule



/*mem.dat文件内容

@001

AB CD

@003

A1

*/

//仿真输出为

Memory[0] = xx;

Memory[1] = AB;

Memory[2] = CD;

Memory[3] = A1;
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_18495139/article/details/106207309

智能推荐

【小常识】从按钮开关看上拉pull-up电阻、下拉电阻_开关需要上拉吗-程序员宅基地

文章浏览阅读1.1w次,点赞18次,收藏61次。https://www.arduino.cn/thread-13186-1-1.html到底啥是上拉(pull-up)電阻和下拉(pull-down)電阻 ?在用 Arduino 做實驗時, 按鈕開關(Button/Switch)是很常見的應用,然後你常常看到文件說要接個電阻, 又看到&quot;上拉電阻&quot;或&quot;下拉電阻&quot;!電阻(resistor)應該大家多多少少都知道(大家都聽過歐姆定律),可是到..._开关需要上拉吗

如何快速有效的学习新领域知识_新领域学习新知识-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏3次。如何快速有效的学习新领域知识 工作以后,对于一些领域的知识,之前我们没并不熟悉或知之甚少,比如想学习做产品,学编程,学习炒股票,学习练瑜伽,这个时候想要去学习,那如何在比较短的时间内学会自己所需要的知识呢?方法无外乎两种:一是自学,二是参加培训班 参加培训班,优点是有学校帮你安排课程,有老师教,有同学可以交流,这样当然会学的快一点,但缺点就是费用高,培训班质量鱼龙混杂,自己也没有那么多时间去上课。..._新领域学习新知识

Django的基本使用【后台管理、视图、模板】(第三天)_django html博客后台数据管理模板-程序员宅基地

文章浏览阅读681次。一、后台管理1)本地化:到项目下面的settings.py配置后台管理的语言和时区LANGUGE_CODE = ‘zh-hans’ #表示使用中文TIME_ZONE = ‘Asia/Shanghai’ #表示中国时间2)创建管理员命令:python manage.py createsuperuser根据提示输入后面前台管理界面需要用到的账号名跟密码。3)注册模型类在应用下的a..._django html博客后台数据管理模板

Maven仓库的地址和镜像加速器及未完整下载的依赖删除_镜像地址和依赖怎么删除-程序员宅基地

文章浏览阅读128次。1.setting.xml文件设置项指定maven仓库位置<localRepository>D:\repository\maven</localRepository>阿里云镜像加速<mirror> <id>nexus-aliyun</id> <mirrorOf>central</mirror..._镜像地址和依赖怎么删除

CSS基础:盒子模型&padding撑大盒子_给一个宽高200px的盒模型添加padding:50px 为了保证盒模型不被撑大 共有几种-程序员宅基地

文章浏览阅读1k次。<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> /* 盒子模型分为三个部分:从内至外分别是:盒子内容、盒子内边距padding、盒子边框border、盒子外边距margin 每个属性都有四个方向:上top、右right、下bottom、左left*/ ._给一个宽高200px的盒模型添加padding:50px 为了保证盒模型不被撑大 共有几种

练习盲打的网站 / 练习打字的网站-程序员宅基地

文章浏览阅读674次。最近找到了一个很好的练习盲打的网站:https://www.typingclub.com/转载于:https://www.cnblogs.com/qiuchengrui/p/10234360.html_电脑闭眼打字练习网站

随便推点

黑群晖DSM7.X SynologyPhotos人脸识别 视频无缩略图_synology photos黑群相册视频不显示缩略图咋整-程序员宅基地

文章浏览阅读240次。仅适用于X86_64的黑群晖7.0-7.2机型,目前测试识别正常,索引期间CPU占用率也不高。原理修改为不调用GPU显卡,只用CPU,所以不算完美!仅测试了DS918+、DS920+,其它机型自行研究!像万由NS202直接支持face识别,不需要修复,BIOS里面关闭vt-d即可!所以建议先研究下实在不行再搞补丁。如果CPU不带核显但安装机型支持核显,肯定要搞这个补丁了!安装机型原版CPU不带核显也不需要折腾补丁。操作步骤1、先停用Synology Photos套件。_synology photos黑群相册视频不显示缩略图咋整

ZooKeeper分布式锁实现_zookeeper 分布式锁 尚硅谷代码-程序员宅基地

文章浏览阅读100次。前言在平时我们对锁的使用,在针对单个服务,我们可以用 Java 自带的一些锁来实现,资源的顺序访问,但是随着业务的发展,现在基本上公司的服务都是多个,单纯的 Lock或者Synchronize 只能解决单个JVM线程的问题,那么针对于单个服务的 Java 的锁是无法满足我们业务的需要的,为了解决多个服务跨服务访问共享资源,于是就有了分布锁,分布式锁产生的原因就是集群。正文实现分布式锁的方式有哪些呢?分布式锁的实现方式主要以(ZooKeeper、Reids、Mysql)这三种为主今天我们主要讲_zookeeper 分布式锁 尚硅谷代码

Android调试报错:The application could not be installed: INSTALL_FAILED_ABORTED-程序员宅基地

文章浏览阅读4.4k次。问题描述:Session ‘app’: Installation did not succeed. The application could not be installed: INSTALL_FAILED_ABORTED华为手机无法运行apk,android studio 连接真机运行时报错解决一:是否是由于手机上的app与我们现在安装的版本冲突 卸载手机上app重新安装解决...

pytthon try except 捕获异常_except pytheon-程序员宅基地

文章浏览阅读484次。try: 理解它是扫描器,将可能出现异常的代码放入其中; 如果在执行过程中出现异常对象了,扫描器会立即察觉到此异常对象, 但是它没有处理它的能力,所以会将异常对象给到except(捕获器)进行解决except: 理解它是捕获器,后面可以定义异常类型,并且和as关键字配合使用; 定义多个except也是合法的,但是它的执行顺序,由上往下,一旦匹配上就执行e..._except pytheon

程序可以运行在模拟器中,但是签名后不能运行在真机上--报错Error starting_已经签了名的为什么会提示未签名,无法在真机上运行-程序员宅基地

文章浏览阅读2.6k次。程序可以运行在模拟器中,但是签名后不能运行在真机上--报错Error starting DemoApp: 'DemoApp' may not contain classes in com.rim, net.rim, net.blackberry, java or javax packages. 解决办法:把项目DemoApp中的com.rim, net.rim, net.blackberry, java or javax 开头的包改名即可。

PAT Basic Level 1031 查验身份证 解题思路及AC代码_身份证检验ac是什么-程序员宅基地

文章浏览阅读90次。基础练习题,涉及数组、字符串的使用_身份证检验ac是什么

推荐文章

热门文章

相关标签