Veirlog学习记录-6-数字频率计的设计与实现(附完整工程)

FPGA的课程的大作业,我们选的是数字频率计设计。下面分享一下代码,还有工程文件,还有自己写的论文(里面会有更加详细的介绍),希望可以对你有所启发。

工程文件github地址

开发环境:Vivado 2015.4+Modelsim(仅用于仿真波形)
开发板:赛灵思公司 xc7a100tcsg324-1

总体设计要求:

  • 可测量脉冲信号的频率
  • 被测信号由100MHz的系统时钟分频获得,频率为学号*1000 Hz
  • 测量结果在6位数码管上显示,高位若是零则不显示该位
  • 采用连续测量方式,每4秒为1个周期,其中1秒用于测量,3秒用于显示

总体设计框图:

子模块设计:

- 分频模块:

结构图:
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module div(
input clk_100mhz, //系统给定的时钟频率
output clk_190hz, //数码管的动态扫描频率
output reg clk_1Hz, //控制模块的驱动频率
output reg fin //输出待测试信号的频率
);
reg [9:0] cnt0;
reg [30:0] cnt;
reg [18:0] cnt1;

always @(posedge clk_100mhz)
cnt1 = cnt1 + 1;
assign clk_190hz = cnt1[18];
always @(posedge clk_100mhz)
if(cnt == 50000000) begin
cnt = 0;
clk_1Hz = ~clk_1Hz;
end
else
cnt = cnt + 1;
always @(posedge clk_100mhz)
if(cnt0 == 499) begin //生成的测试信号的频率为100KHz
cnt0 = 0;
fin = ~fin;
end
else
cnt0 = cnt0 + 1;

endmodule

被测频率本来要求是学号*1000Hz,但是我的学号无法被整除,所以无法度量频率计的的精度,更无法进行误差分析。所以才选取了100KHz作为测试频率。

- 控制模块

结构图:

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
module control(clk_1Hz, rst, count_en, latch_en, clear);  
input clk_1Hz;
input rst; //复位信号
output count_en; //计数使能
output latch_en; //锁存使能
output clear; //清零信号
reg [2:0] state; //状态信号,用于控制各种使能信号
reg count_en;
reg latch_en;
reg clear;

always @(posedge clk_1Hz or negedge rst)
if(!rst) //复位信号有效
begin //各种使能信号清零
state <= 3'b000;
count_en <= 1'b0;
latch_en <=1'b0;
clear <= 1'b0;
end
else //遇到基准信号的下一个上升沿,状态变化一次,每次变化后状态持续1s
begin
case(state)
3'b000:
begin //第一个上升沿到达,开始计数,计数1个基准信号周期内待测信号的上升沿个数,此个数即为待测信号的频率
count_en <= 1'b1; //计数使能信号有效
latch_en <= 1'b0;
clear <= 1'b0;
state <= 3'b001;
end
3'b001:
begin //第二个上升沿到达,计数完成,锁存使能信号有效,测得频率锁存至锁存器中
count_en <= 1'b0;
latch_en <=1'b1;
clear <= 1'b0;
state <= 3'b010;
end
3'b010:
begin //第三个上升沿到达,计数完成,锁存使能信号有效,测得频率锁存至锁存器中
count_en <= 1'b0;
latch_en <=1'b1;
clear <= 1'b0;
state <= 3'b011;
end
3'b011:
begin //第四个上升沿到达,计数完成,锁存使能信号有效,测得频率锁存至锁存器中
count_en <= 1'b0;
latch_en <=1'b1;
clear <= 1'b0;
state <= 3'b100;
end
3'b100:
begin //第五个上升沿到达,清零使能信号有效,计数器清零,为下一次计数做准备
count_en <= 1'b0;
clear <= 1'b1;
latch_en <=1'b1;
state <= 3'b000; //状态清零,进入下一次测量
end
default:
begin
count_en <= 1'b0;
latch_en <=1'b0;
clear <= 1'b0;
state <= 3'b000;
end
endcase
end

1
endmodule

- 计数模块

结构图:

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module counter_10(en_in, rst, clear, fin, en_out, q);  
input en_in; //输入使能信号
input rst; //复位信号
input clear; //清零信号
input fin; //待测信号
output en_out; //输出使能,用于控制下一个计数器的状态,当输出使能有效时,下一个模10计数器计数加1
output [3:0] q; //计数器的输出,4位BCD码输出

reg en_out;
reg [3:0] q;

always@ (posedge fin or negedge rst) //输入待测信号的上升沿作为敏感信号
if(!rst) //复位信号有效,计数器输出清零
begin
en_out <= 1'b0;
q <= 4'b0;
end
else if(en_in) //进位输入使能信号有效
begin
if(q == 4'b1001) //若q = 4'b1001的话,q清零,同时进位输出使能有效,即en_out 赋值为1'b1
begin
q <= 4'b0;
en_out <= 1'b1;
end
else //若q未达到4'b1001时,每到达待测信号的一个上升沿,q加1,同时输出进位清零
begin
q <= q + 1'b1;
en_out <=1'b0;
end
end
else if(clear) //若清零信号有效,计数器清零,用于为下一次测量准备
begin
q <= 4'b0;
en_out <= 1'b0;
end
else
begin
q <= q;
en_out <=1'b0;
end

endmodule

锁存模块

结构图:

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module latch(clk_1Hz, latch_en, rst, q0, q1, q2, q3, q4, q5, q6, q7,  
d0, d1, d2, d3, d4, d5, d6, d7);
input clk_1Hz, latch_en, rst;
input[3:0] q0, q1, q2, q3, q4, q5, q6, q7;
output[3:0] d0, d1, d2, d3, d4, d5, d6, d7;
reg[3:0] d0, d1, d2, d3, d4, d5, d6, d7;
always@ (posedge clk_1Hz or negedge rst)
if(!rst) //复位信号有效时输出清零
begin
d0 <= 4'b0; d1 <= 4'b0; d2 <= 4'b0; d3 <= 4'b0; d4 <= 4'b0;
d5 <= 4'b0; d6 <= 4'b0; d7 <= 4'b0;
end
else if(latch_en) //锁存信号有效时,将计数器的输出信号锁存至锁存器
begin
d0 <= q0; d1 <= q1; d2 <= q2; d3 <= q3; d4 <= q4;
d5 <= q5; d6 <= q6; d7 <= q7;
end
else //上面两种情况均未发生时,输入不变
begin
d0 <= d0; d1 <= d1; d2 <= d2; d3 <= d3; d4 <= d4;
d5 <= d5; d6 <= d6; d7 <= d7;
end

endmodule

显示模块

结构图:

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
module IP_seg_disp(
input clk_190hz,
input [3:0] d0,d1,d2,d3,d4,d5,d6,d7
output reg [7:0] duan,
output reg [7:0] wei
);

reg [3:0] disp;
reg [2:0] smg_ctl;

always @ ( posedge clk_190hz)
smg_ctl = smg_ctl + 1'b1;
always @ (*)
case ( smg_ctl )
3'b000:begin
wei = 8'b11111110;
disp = d0 [3:0];
end
3'b001:begin
wei = 8'b11111101;
disp = d1 [3:0];
end
3'b010:begin
wei = 8'b11111011;
disp = d2 [3:0];
end
3'b011:begin
wei = 8'b11110111;
disp = d3 [3:0];
end
3'b100:begin
wei = 8'b11101111;
disp = d4 [3:0];
end
3'b101:begin
wei = 8'b11011111;
disp = d5 [3:0];
end
3'b110:begin
wei = 8'b10111111;
disp = d6 [3:0];
if( disp==0 ) //如果高位数值为0,则不显示该位
disp <=4'b1111;
end
3'b111:begin
wei = 8'b01111111;
disp = d7 [3:0];
if( disp==0 )
disp <=4'b1111
end
default:;
endcase

always @ ( * )
case (disp)
4'b0000:duan = 8'b11000000;
4'b0001:duan = 8'b11111001;
4'b0010:duan = 8'b10100100;
4'b0011:duan = 8'b10110000;
4'b0100:duan = 8'b10011001;
4'b0101:duan = 8'b10010010;
4'b0110:duan = 8'b10000010;
4'b0111:duan = 8'b11111000;
4'b1000:duan = 8'b10000000;
4'b1001:duan = 8'b10010000;
4'b1010:duan = 8'b10001000;
4'b1011:duan = 8'b10000011;
4'b1100:duan = 8'b11000110;
4'b1101:duan = 8'b10100001;
4'b1110:duan = 8'b10000110;
4'b1111:duan = 8'b11111111;
default:duan = 8'b11000000;
endcase
endmodule

顶层模块

code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
module freDetect(clk_100mhz,rst,duan,wei);  
input clk_100mhz;
input rst; //复位信号
output [7:0] duan;
output [7:0] wei;
wire[3:0] q0, q1, q2, q3, q4, q5, q6, q7; //中间数据
wire[3:0] d0, d1, d2, d3, d4, d5, d6, d7;
wire fin;
wire clk_1Hz;
wire clk_190hz;
//分频模块实例
div u_div(
.clk_1Hz(clk_1Hz),
.clk_190hz(clk_190hz),
.clk_100mhz(clk_100mhz),
.fin(fin)
);
//显示模块实例
IP_seg_disp u_IP_seg_disp( .clk_190hz(clk_190hz), .d0(d0),
.d1(d1), .d2(d2), .d3(d3), .d4(d4),
.d5(d5), .d6(d6), .d7(d7),
.duan(duan), .wei(wei)
);
//控制模块实例
control u_control(.clk_1Hz(clk_1Hz), .rst(rst), .count_en(count_en),
.latch_en(latch_en), .clear(clear));

//计数器模块实例
counter_10 counter0(.en_in(count_en), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out0), .q(q0));
counter_10 counter1(.en_in(en_out0), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out1), .q(q1));
counter_10 counter2(.en_in(en_out1), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out2), .q(q2));
counter_10 counter3(.en_in(en_out2), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out3), .q(q3));
counter_10 counter4(.en_in(en_out3), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out4), .q(q4));
counter_10 counter5(.en_in(en_out4), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out5), .q(q5));
counter_10 counter6(.en_in(en_out5), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out6), .q(q6));
counter_10 counter7(.en_in(en_out6), .clear(clear), .rst(rst),
.fin(fin), .en_out(en_out7), .q(q7));

//锁存器模块实例
latch u_latch(.clk_1Hz(clk_1Hz), .rst(rst), .latch_en(latch_en),
.q0(q0), .q1(q1), .q2(q2), .q3(q3), .q4(q4), .q5(q5),
.q6(q6), .q7(q7), .d0(d0), .d1(d1), .d2(d2), .d3(d3),
.d4(d4), .d5(d5), .d6(d6), .d7(d7));

endmodule

测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module freDetect_tb;  
parameter CLK_1HZ_DELAY = 50000000; //用于生成1Hz基准信号
parameter FIN_DELAY = 500; //用于生成100KHz待测信号
reg clk_1Hz;
reg fin;
reg rst; //复位
wire[3:0] d0, d1, d2, d3, d4, d5, d6, d7;
initial
begin
rst =1'b0;
#1 rst = 1'b1;
end
initial
begin
fin = 1'b0;
forever
#FIN_DELAY fin = ~fin;
end
initial
begin
clk_1Hz = 1'b0;
forever
#CLK_1HZ_DELAY clk_1Hz = ~clk_1Hz;
end
freDetect freDetect1(.clk_1Hz(clk_1Hz), .rst(rst), .fin(fin),
.d0(d0), .d1(d1), .d2(d2), .d3(d3), .d4(d4), .d5(d5), .d6(d6), .d7(d7));
endmodule

波形仿真结果如图所示:
结果显示
实验结果:

两张图片分别为 测量计数时和复位时和显示计数时:
测量计数时和复位时的显示显示计数时

我也写了几篇关于Veirlog的文章,感兴趣的同学可以去看看。该模块链接如下:
Verilog学习

Veirlog学习记录-5-循环移位数码管的设计与实现

实现功能:在开发板的数码管上显示特定数字,并且让这些数字循环移位。

总体框图如下:
总体框图

代码如下:

分频模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module div(
input clk, //输入自带的系统时钟(100Mhz)
input rst, //复位
output scan_clk //输出时钟,用于驱动数码管,让其动态扫描用
);
reg [19:0] clkdiv;

always @(posedge clk or posedge rst)
begin
if( rst == 1) clkdiv <=0;
else clkdiv <= clkdiv + 1;
end
assign scan_clk = clkdiv[15]; //使得san_clk = 190Hz
endmodule

其他模块(显示模块):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
module otherModel(
input rst,
input scan_clk,

output reg [7:0] an,
output reg [6:0] seg
);
reg [3:0] digit; //选择位上显示的数字
reg [2:0] cnt; //选择哪一位显示

always @(posedge scan_clk or posedge rst)
begin
if ( rst == 1) cnt <=0;
else cnt <=cnt + 1;
end

always @ (*)
begin
case ( cnt )
3'b000:begin digit = 4'b0000; an = 8'b01111111; end
3'b001:begin digit = 4'b0001; an = 8'b10111111; end
3'b010:begin digit = 4'b0010; an = 8'b11011111; end
3'b011:begin digit = 4'b0011; an = 8'b11101111; end
3'b100:begin digit = 4'b0100; an = 8'b11110111; end
3'b101:begin digit = 4'b0101; an = 8'b11111011; end
3'b110:begin digit = 4'b0110; an = 8'b11111101; end
3'b111:begin digit = 4'b0111; an = 8'b11111110; end
default:begin digit= 4'b0000; an = 8'b01111111; end
endcase
end

always @ (*)
begin
case ( digit )
4'b0000:seg = 7'b0100100;
4'b0001:seg = 7'b1000000;
4'b0010:seg = 7'b1111001;
4'b0011:seg= 7'b0000000;
4'b0100:seg =7'b0100100;
4'b0101:seg =7'b1000000;
4'b0110:seg =7'b1111000;
4'b0111:seg =7'b0000000;
default: seg = 7'b0000001;
endcase
end



endmodule

顶层模块:

1
2
3
4
5
6
7
8
9
10
module top(
input clk,
input rst,
output [7:0] an,
output [6:0] seg
);

div u1(clk,rst,scan_clk); //例化模块
otherModel u2(rst,scan_clk,an,seg);
endmodule

测试文件:
注意:测试文件是针对otherModel文件的。在写测试文件之前,最好不要添加顶层模块,否则测试时会找不到想要测试的那个文件。如果,已经编写完了顶层模块,可以在design列表中,右键要测试的文件,然后点击 设置为顶层文件,这样就会测试到指定的文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module otherModel_tb();
reg rst,scan_clk; //输入信号要用 reg型
wire [7:0] an; //输出信号要用 wire型
wire [6:0] seg; //输出信号

otherModel test(
.rst(rst),
.scan_clk(scan_clk),
.an(an),
.seg(seg)

);
initial fork
scan_clk = 0;
rst = 1; #50 rst = 0;
join

always #10 scan_clk = ~scan_clk; //定义没过10毫秒,信号就会进行翻转

endmodule

仿真波形如下:
仿真结果
我也写了几篇关于Veirlog的文章,感兴趣的同学可以去看看。该模块链接如下:
Verilog学习

matlab实现随机攻击网络节点+蓄意攻击网络节点(2)

更新:

有同学反馈说:网络效率的函数可能有点问题。可以试试这个网络效率函数,对应的部分改一下就可以了。

还有最大连通子图比例函数:最大连通子图比例函数




上一篇介绍了随机攻击网络节点与蓄意攻击节点的基本方法。上一篇文章地址:matlab实现随机攻击网络节点+蓄意攻击网络节点(1)

其中随机攻击的部分还有一些瑕疵,就是在实际的研究中,需要对网络进行多次(数十次甚至上百次)攻击后取指标变化平均值,这样的实验数据才具有一定的说服力。

其实这个问题乍一听起来,原理也比较简单:就是让一个程序运行指定的次数然后,累加程序中某一个变量后取平均值

实现起来也并不费劲,首先就是把这个程序定义为一个函数,然后把所需要累加的变量作为函数的返回值。然后在另一个文件中创建循环,在循环中调用该函数,用一个变量接收该函数的返回值,以达到累加的效果,最后在循环外部取一个平均值即可。

代码如下所示:

定义函数文件名称(该名称需要与函数名称相同)

函数的参数介绍:

输入值str:意为数据文件的路径;numDelete:删除节点的个数(这里的命名只是为了方便并不是必选项)
返回值Eglob,即网络效率值的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Eglob = ATestAver(str,numDelete)
%输入 :
% str:意为数据文件的路径
% numDelete:删除节点的个数

%返回值:Eglob,即网络效率值的数组

%加载数据文件
load(str);

Name_Struct = Node_Key_Sort_Descend; % Name_Struct 数据集名称,更换网络数据集时,需要更改此处结构体名称
A_Init = Name_Struct.Adjacent_Matrix; %% 网络邻接矩阵
N_Init = size(A_Init,1); %% 节点个数

NetEff_Init = zeros(1,numDelete);
Struct_Init = struct('Deg',NetEff_Init);

% 初始网络性能
%生成随机数,以此进行随机攻
Name_Struct.Node_Key_Degree = randperm(440);

%%
% 按照 Degree 算法排序,删除节点
A = A_Init; %% 网络邻接矩阵 A
for i = 1:numDelete
% 按照 Degree 算法排序,删除节点
end

定义测试文件:

文件中需要定义随机攻击的次数和随机攻击节点的个数,具体参数设置应视具体网络而定。
在调用函数时,要传入文件路径,和删除节点的个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%定义随机攻击节点的个数,具体数值根据网络规模拟定
numDelete = 22;

%定义网络效率初始矩阵
netSum = zeros(1,numDelete);

%定义随机攻击的次数,也就是函数循环的次数
numRandom = 50;
for i=1:numRandom

%把得到的网络效率数组赋给netI
netI = ATestAver('Data\12_15jiaQuanData.mat',numDelete);
%累加
netSum = netSum + netI;
end

%求出平均值
netAver = netSum/numRandom;

我的测试数据具有小世界特性与无标度特性。并得到了以下的结果:
例子
大家可以看出,经过多次随机攻击取指标平均值之后,曲线近似于一条直线,下降速率较小且符合实际的网络情况。验证了无标度网络对于随机攻击拥有较好的鲁棒性。

希望本文对大家有所帮助,有任何问题或者是建议,欢迎大家与我交流。

matlab实现随机攻击网络节点+蓄意攻击网络节点(1)附github完整工程地址

更新:

有同学反馈说:网络效率的函数可能有点问题。可以试试这个网络效率函数,对应的部分改一下就可以了。

还有最大连通子图比例函数:最大连通子图比例函数


在研究网络的鲁棒性的时候,我们往往会通过随机与蓄意攻击网络节点,观察网络效率的下降比例来进行网络特性的研究。

常见的指标有:最大连通子图比例、网络效率、平均距离等等。
这三个指标是不同的,但是实现随机攻击与蓄意攻击的原理是相同的,这里以按照节点度攻击的网络效率变化为例(其他两个指标就是函数不同,想按照其他节点重要度排序指标,也是类似的,只需要按照想要的排序方法得出节点的排序即可)。

就是按照节点的重要性排序,通过循环来删除节点。把临界矩阵中节点对应的行和列先置0,然后再删除。每删除一次节点,就生成了一个新的邻接矩阵,然后每一次都通过testEglob函数计算出当前的网络效率值。

首先需要准备的数据如下:

网络的邻接矩阵,节点度的排序(从大到小排名,度大的排名靠前)。

节点度的排名要按照节点的编号排序,下图是一个简单的例子,建议先在Excel中排列好了,然后再复制到Matlab中转置一下保存为mat文件就可以了。

度排序示例

明白了蓄意攻击的原理,那么随机攻击的原理也比较好理解了,蓄意攻击是按照节点重要度排序进行的攻击,那么随机攻击可以理解为给所有节点随机赋排名,所以攻击的时候就等效于随机攻击了。也就是说,在随机攻击时,你只需要在蓄意攻击的基础上添加一行代码,把度排序的数组赋值上长度相同的一个随机数组,即:

1
Name_Struct.Node_Key_Degree = randperm(440);

具体代码如下:

主函数: testRandom(命名随意。。。)
作用:原理挺简单的,就是通过循环来删除节点。把临界矩阵中节点对应的行和列先置0,然后再删除。每删除一次节点,就生成了一个新的邻接矩阵,然后每一次都通过testEglob函数计算出当前的网络效率值。

部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
% 蓄意攻击:按照节点重要性顺序,一次攻击一个节点

clc;
clear;


% 初始网络性能
%生成随机数,以此进行随机攻击(注释掉即为蓄意攻击),随机数值改为你自己网络的节点数
Name_Struct.Node_Key_Degree = randperm(440);

%%
% 按照 Degree 算法排序,删除节点
A = A_Init; %% 网络邻接矩阵 A
B=[]; %%定义空数组,接收被删除的节点序号

for i = 1:NumDelete
%% 删除节点 Node_Key_Degree(i),用 0 占位,不能置空
B(i) = Name_Struct.Node_Key_Degree(i);
Con_Index_NetEff = testEglob( AA );
Eglob(i) = Con_Index_NetEff.Net_Eff_Mymod;
end

%接下来就是生成网络连通效率图
%Eglob存储了相应的网络效率的数值

正常情况下,一次随机攻击并不能说明什么,一次随机攻击的数据也并不可靠,所以需要多次随机攻击之后取平均值,这样得出的数据才更具有说服力,下一篇将介绍如何实现,matlab实现随机攻击网络节点+蓄意攻击网络节点(2)

本篇文章对应的GitHub工程地址如下:内含函数文件+测试文件,下载即可直接运行。

工程地址

希望对大家有所帮助,有任何疑问欢迎与我交流,谢谢你的时间。

Veirlog学习记录(4)分频模块+层次化的模块设计

 前三篇文章都是单一模块的设计,然后测试是否来完成特定的功能,不过这种方法只能解决一些简单的问题,所以实际中需要层次花的设计。

​ 这次给大家分享的就是一个层次化的设计,移位寄存器模块+分频模块,然后再用顶层文件把它们组合在一起。

​ 寄存器模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module shift1(clk,D,MD,QB);

input clk; //时钟信号
input [7:0] D; //预置数,可以在测试文件中预置,或者在板子上选取
input [1:0] MD; //模式选择数值
output [7:0] QB; //输出的数值
reg [7:0] REG; //定义寄存器类型

always @ ( posedge clk ) begin //对时钟信号上升沿敏感
case (MD) //根据MD的数值,选择模式
2'b01: begin REG[0] <= REG[7] ; REG[7:1] <= REG[6:0]; end //循环左移
2'b10: begin REG[7] <= REG[0] ; REG[6:0] <= REG[7:1]; end //循环右移
2'b00: begin REG <= D; end //加载预置的数值

endcase
end
assign QB[7:0] = REG[7:0]; //把REG中的数值赋给QB

endmodule

​ 分频模块:

​ 记得一开始的十进制可加可减计数器设计中,我们没有用分频模块,而是把时钟模块绑到了一个按键上,然后按一次表示一个时钟,这样很麻烦,引入分频模块就很有必要了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module div(
input div_rst,
input div_clk, //输出的时钟是100M的
output clk_out
);
reg [30:0] div_q;
reg temp;
always @ (posedge div_clk or posedge div_rst)
begin
if ( div_rst == 0 ) div_q <= 0;
else if( div_q == 50000000) //保证输出的时钟是1s一次的
begin
div_q <= 0; temp <= ~temp;
end
else div_q <= div_q + 1;
end
assign clk_out = temp;
endmodule

​ 顶层文件:

1
2
3
4
5
6
7
8
9
10
11
12
module top(
input clk,
input rst,
input [7:0] D,
input [1:0] MD,
output [7:0] QB);

wire a; //这个a就是把分频模块输出的时钟信号接入到寄存器模块中
div u1 (rst,clk,a); //把模块和顶层连接起来,子模块名字要和之前起的一致
shift1 u2 (a,D,MD,QB);//要注意:这里输入输出端的排布顺序要和在子模块写的顺序一致,否则就会报错。

endmodule

注意:
测试子模块时,比如寄存器模块,那么写完这个模块时,就应该编译,然后写测试文件直接就测试,不要先等写完顶层模块再测试,否则系统会默认测试的是顶层文件,可能会导致没有任何输出产生的情况。

​ 如果没有什么问题,你写顶层文件,然后保存,会出现如下的样子:

​ 表明顶层文件和子模块联系成功,编译没有错误之后,就可以进行下一步了。顶层模块也可以不用测试,如果不写测试文件的话,可以选择 综合下面的:

​ 然后就可以看形成的逻辑图,再比对是否哪里有问题,按照以上程序设计会生成以下的图:

移位寄存器模块如何编写测试文件,可以参考如下链接:
https://blog.csdn.net/weixin_43877139/article/details/88851235

我也写了几篇关于Veirlog的文章,感兴趣的同学可以去看看。该模块链接如下:
Verilog学习

阿里云Ubuntu18.04服务器安装Mono_C#开发环境

重要:安装Mono一定要参照官方文档

一开始安装的时候走了很多的弯路,就是没看官方帮助文档。实际上当你想安装什么东西,首选就是官方文档,其次才是别人的经验把。

官方帮助文档:https://www.mono-project.com/download/stable/#download-lin

这里面介绍的很清楚:对于不同的系统也有自己的安装方法。

第一种方法:直接安装

第一步:

1
sudo apt-get update

第二步:

1
2
3
4
5
6
7
sudo apt install gnupg ca-certificates

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF

echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list

sudo apt update

第三步:

根据自己的需要,可以根据官方文档中的提示,选择安装不同的类库。这种方式可能会出现下载速度过慢的情况,可以把安装源换为国内的,然后使用,具体请Goole或者百度。

第二种:解压缩方式或者直接从网站安装

我这里选择的是5.20.1.19版本,你可以自行选择下载安装的版本。
下载地址:http://download.mono-project.com/sources/mono/

第一步:

1
sudo apt-get update

第二步:

1
2
3
4
5
6
7
8
9
10
11
12
13
cd /usr/local/src/

sudo wget http://download.mono-project.com/sources/mono/mono-5.20.1.19.tar.bz2

tar -jxvf mono-5.20.1.19.tar.bz2

cd mono-5.20.1.19

sudo ./configure --prefix=/usr

sudo make

sudo make install

sudo make这一步执行之后,可能提示你缺少 cmake 这个依赖。执行

1
sudo apt install cmake

之后继续步骤就可以了。

这些步骤执行之后,可以输入 mono -V 查看版本信息。如下图一样便是成功了。

实际上感觉在ubuntu系统的服务器发布C#写的网页还是比较费劲的,推荐还是用Windows server系统安装IIS来发布Asp.net网站。最近我也在弄这个,有兴趣的朋友可以去看我发的相关内容。

Veirlog学习记录(3)--移位寄存器(左循环,右循环)的实现

移位寄存器的设计:
有三个模式:

  • 左循环

  • 右循环

  • 加载预置的数

  • 具体功能可以根据需要对程序做出一些修改即可

    代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module shift1(clk,D,MD,QB);

input clk; //时钟信号
input [7:0] D; //预置数,可以在测试文件中预置,或者在板子上选取
input [1:0] MD; //模式选择数值
output [7:0] QB; //输出的数值
reg [7:0] REG; //定义寄存器类型

always @ ( posedge clk ) begin //对时钟信号上升沿敏感
case (MD) //根据MD的数值,选择模式
2'b01: begin REG[0] <= REG[7] ; REG[7:1] <= REG[6:0]; end //循环左移
2'b10: begin REG[7] <= REG[0] ; REG[6:0] <= REG[7:1]; end //循环右移
2'b00: begin REG <= D; end //加载预置的数值

endcase
end
assign QB[7:0] = REG[7:0]; //把REG中的数值赋给QB

endmodule

测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module shift1_tb();
reg clk;
reg [7:0] D;
reg [1:0] MD;
wire [7:0] QB; //输出

initial
begin //初始化
clk = 0;
D = 8'b11110000; //初始化待移数值(这样赋值只是为了最后好观察波形)
MD = 2'b00; //以置数模式开始
end

always #10 clk = ~clk; //时钟信号
always #100 MD = MD+1;//循环改变模式,

shift1 test( .clk(clk),
.D(D),
.MD(MD),
.QB(QB)
);

endmodule

这里图形没有从一开始截取,而是选择能看到三个模式效果的时间段,MD=0时,一直在加载预置数,输出等于D 11110000.MD=1时,开始循环左移。MD=2时,开始循环右移。

基本的设计就是这样子,可以在它的基础上进行改进,以达到大家需要的功能,比如在shift1中的case加几个模式。D的值也可以随便赋值。

我也写了几篇关于Veirlog的文章,感兴趣的同学可以去看看。该模块链接如下:
Verilog学习

Veirlog学习记录(2)--十(多)进制可加可减计数器的实现

上一篇实现的是,十(多)进制的加计数。

这次是要加上减计数,也就是实现可加可减的操作,并且在板子上验证,这次设计没有加分频模块,时钟脉冲是用按键控制的,加分频也可以。

如果要加上一个减的功能,就是要在加计数的基础上加一个使能端up_down,使能端为1的时候,自加。为0的时候自减。同时从0减到9的时候要有一个借位。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
module M10_updown( 
input EN,CP,Rd,up_down, //up_down=1时加计数,up_down=0时减计数
output reg Cout,
output reg [3:0] Q
);
always @ (posedge CP,negedge Rd)

if( ~Rd ) Q<=4'b0000;
else if(EN&&up_down) //当使能端为1,且选择加计数时,开始加计数
begin
if(Q<4'b1001) Q<=Q+1'b1; //判断是否加到了9
else Q<=4'b0000;
end
else if ( EN && ~up_down )//当使能端为1,且选择加计数时,开始减计数
begin
if ( Q>4'b0000) Q<=Q-1'b1;//判断是否减到了0
else Q<= 4'b1001;
end
else Q<= Q;
always @ (Q)

if(Q==4'b1001 && up_down) Cout = 1;//当加计数时 加到了9,进位
else if(Q==4'b0000 && ~up_down) Cout = 1;//当减计数时 减到了9,借位
else Cout=0;

endmodule

测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module M10_updown_tb();
reg EN,CP,Rd,up_down; //输入端口说明
wire Cout; //进位信号
wire [3:0] Q; //数据输出端口及变量的数据类型生命
M10_updown test( .EN(EN), //M10_updown这的名称要和design的文件名一致
.CP(CP),
.Rd(Rd),
.up_down(up_down),
.Cout(Cout),
.Q(Q) );
initial fork
CP=0;
EN=1; #20 EN=0;#50 EN=1;
Rd=0; #30 Rd=1;
up_down=0; #300 up_down=1;
join
always #8 CP=~CP;

endmodule

仿真图如下:

然后就是布局布线,添加约束文件。接上板子就ok了。

我也写了几篇关于Veirlog的文章,感兴趣的同学可以去看看。该模块链接如下:
Verilog学习

算法图解第四章笔记:快速排序1(java版)

分而治之(D&C)一种著名的递归式问题的解决方法。

案例:农场主分地问题:

要求:要将这块地均匀分成方块,且分出的方块尽可能大。

步骤:(1)找出基线条件,这种条件必须尽可能的简单

​ (2)不断将问题分解(或者说是缩小规模),直到符合基线条件

这个案例很有意思,而且后面的顿悟时刻:为何不对余下的那一块小块地使用相同的算法呢?这正是递归的核心思想啊。

另一个小例子:

​ 给定一个数组:2,4,6

​ 需要将这些数字相加,并返回结果。使用循环很容易。

1
2
3
4
5
6
7
public static int sum(int[] arr){
int total = 0;
for(int i = 0;i < arr.length ; i++){
total += arr[i];
}
return total;
}

如何用递归完成这种任务呢?

第一步:找出基线条件。最简单的数组:不包含任何元素或只包含元素,这样计算总和非常容易。

第二步:每次递归调用都必须离空数组更进一步。

那么如何缩小问题的规模呢?给sum传递的数组更短。

练习:

4.1:实现前述sum函数

分析:这个问题基线条件比较清晰,不太清晰的点其实是递归条件,如何让数组离空数组更近一步呢?一开始我想的是,把数组截取,让数组长度每次调用都减一。其实这样想就有点偏差了,因为让数组变成空数组,其实并不是真的让数组逐渐的变为空数组,其实一直都是原来的数组,只不过每次调用的元素都是数组中的不同元素。下面的代码可能会更加帮助理解递归条件:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 练习4.1:利用递归求和:
* 基线条件: 最简单的数组:不包含任何元素或者只包含一个元素
* 递归条件: 给函数传递的数组越来越短
*
*/
public static int sum_1(int[] arr,int n){
//基线条件
if( n == 0 ){
return arr[n];
}else{
return arr[n] + sum_1(arr,n-1);
}

}

4.2:编写一个递归函数来计算列表包含的元素数

分析:乍一看这个问题和4.1不太一样,但是如果数组中每个元素的值都是1的话,是不是这时候求数组的和,得到的就是列表中所包含的元素数。所以只需要把上述的代码稍做修改即可。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* 练习4.2:编写函数来计算列表所包含的元素数
*
*/
public static int countNumbers(int[] arr,int n){
//判断数组是否为空
if(arr.length == 0){
return 0;
}
//基线条件
else if(n == 0){
return 1;
}else{
return 1 + countNumbers(arr, n-1);
}

}

4.3:找出列表中最大的数字

分析:这个问题就比前两个深化一些了,要找出列表中的最大值,其实有很多方法。如果用递归去做,其实还是要考虑基线条件和递归条件。和前两个问题类似,递归条件还是想让传入的数组的长度原来越短,换句话说就是,想要比较的元素数越来越少。这里可以把一个数组分割成为两个,通过数组的下标做判断。左数组找出一个最大值,右数组找一个最大值,再进行比较,返回大的那个。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static int findMax(int[] arr,int leftIndex,int rightIndex){
//基线条件
if(leftIndex == rightIndex){
return arr[leftIndex];
}
//这种方式求得mid不会溢出
int mid = leftIndex + (rightIndex -leftIndex)/2;
int maxLeft = findMax(arr,leftIndex,mid);
int maxRight = findMax(arr,mid+1,rightIndex);

return Math.max(maxLeft, maxRight);
}
}

我的测试数组为2,4,6,8

所得到的输出为:

算法图解第三章笔记:递归(java版)

3.1 递归

案例:寻找祖母的钥匙。

钥匙在祖母的盒子里,一个大盒子中有很多小盒子,小盒子中也可能还有盒子。钥匙就在某个盒子中。如何找到钥匙?

方法一:

  1. 创建一个要查找的盒子堆
  2. 从盒子中取出一个盒子,在里面找
  3. 如果找到的是盒子,就将其加入到盒子堆中,以便以后再查找。
  4. 如果找到钥匙,则大功告成
  5. 回到第二步

方法二

  1. 检查盒子中的每样东西
  2. 如果是盒子,就回到第一步
  3. 如果是钥匙,则大功告成

第一种方法使用while循环:只要盒子堆不空,就从中取一个盒子,并在其中仔细查找。

第二种方法使用递归—函数调用自己。

两种方法作用相同。递归只是让解决方案更加清晰,并没有性能上的优势。有一句是这样说的:“如果使用循环,程序的性能可能更高;如果使用递归。程序可能更容易理解。如何选择要看什么对你来说更重要”。

3.2 基线条件和递归条件

由于递归函数自己调用自己,因此编写这样的函数时很容易出错,进而导致无限循环。

例如:假设你要编写一个像下面这样的倒计时函数:

>3…..2…..1

可以编写的递归函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class Code_02_BaseCase {
public static void main(String[] args) {
countDown(3);
}
/*
* 未添加基线条件,所以程序会一直运行下去
*/
public static void coutDown(int i){
System.out.println(i);
coutDown(i-1);
}
}

当你运行时,这个函数会一直运行下去。。。。

所以编写递归函数时,必须告诉它何时停止递归。正因为如此,每个递归函数都有两部分:基线条件(base case)和递归条件(recursive case)

给函数countDown添加基线条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
* 设计一个倒计时3、2、1的计时器
*
* 基线条件:
*
*/
public class Code_02_BaselineCondition {
public static void main(String[] args) {
countDown(3);
}

/*
* 添加基线条件
*/
public static void countDown(int i){
System.out.println(i);
//基线条件
if(i <= 1 ){
return;
}else{
//递归条件
countDown_1(i-1);
}
}

}

打印输出为:

3.3 栈

​ 调用栈 call stack

​ 有两种操作:压入(插入)和 弹出(删除并读取)

1.调用栈:

​ 一个简单的函数,来看看计算机是如何调用栈的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 演示计算机是如何调用栈的
*
*/
public class Code_03_CallStack {
public static void main(String[] args) {
greet("John");
}

public static void greet(String name){
System.out.println("hello,"+ name + "!");
greet2(name);
System.out.println("getting ready to say bye....");
bye();
}

public static void greet2(String name){
System.out.println("how are you, "+ name + "?");
}

public static void bye(){
System.out.println("ok bye");
}
}

打印输出为:

一个重要的概念:调用另一个函数时,当前函数暂停并处于未完成的状态。

2.递归调用栈:

递归函数也使用调用栈。下面是一个例子:

factorial(5)写作5!其定义如下:5!= 5 * 4 * 3 * 2 * 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* 递归调用栈:
* 递归函数求解
* 5!= 5*4*3*2*1
*
*/
public class Code_04_RecursionCallStack {

public static void main(String[] args) {
System.out.println(factorical(5));
}
/*
* 递归函数
*/
public static int factorical(int x){
//基线条件
if(x == 1){
return 1;
}else{
//递归条件
return x*factorical(x - 1);
}
}
}

打印输出为:

© 2021 XuXing's blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero