line
小君童靴说头儿给了他一个project,实现给出屏幕上任意两个点,求出这两个点之间直线上的所有的点。觉得这个很好玩,就自己也写了一点code
1 /* 2 date : 2014/10/21 3 version : QuartusII 14.0 + DE1-SOC(cycloneV) 4 function: 输入两个点 Xmax = 1023 Ymax = 511 ,计算出中间点的坐标 5 6 说明 : (1)1023=0x3ff (10bit) 511 = 0x1ff (9bit) 7 (2)直线方程: y=kx+m ; 8 (3)第116行 装载Ymin值的时候-1, 要求输入Ymin不可以小于1:如 9 果这个地方不剪掉1,会导致输出有(Ymax-Ymin)个脉冲的延时 10 (4)现象三的原因是 144 行的判定方法导致。判定当前点为合适点的 11 方式是第二次离直线最近,第一次prev2_dm 较大,第三次curr_dm也较大,116 12 行不减1就无法满足“远,近,远”的判定,从而一直扫到最后都找不到合适点。 13 (5)本程序的计算方式是最直接的运算,计算量大,浪费硬件资源,更 14 好的算法等待大家的指点 15 (6)由于计算输出延时了三个周期,如150行,所以最后三个点无法覆盖到。 16 */ 17 18 19 module line ( 20 clock , 21 reset , 22 xs_in , //输入的X 点的起始坐标 23 ys_in , // 输入的Y 点的起始坐标 24 xe_in , //输入X点的终止坐标 25 ye_in , //输入Y 点的终止坐标 26 in_en , //当前输入信号有效标志 1:有效 0:无效 27 28 x_ou, //输出的X 点的坐标 29 y_ou, // 输出的Y 点的坐标 30 fini_flag //计算完成标志位 31 ); 32 input clock ,reset ; 33 input in_en ; 34 input [9:0] xs_in ,xe_in ; 35 input [8:0] ys_in ,ye_in ; 36 37 output reg [9:0] x_ou ; 38 output reg [8:0] y_ou ; 39 output reg fini_flag ; 40 41 wire signed [15:0] dx ; // X方向上的变化量 42 wire signed [15:0] dy ; //Y方向上的变化量 43 reg signed [15:0] line_k ; // 用来存储线条的斜率 44 reg signed [15:0] line_m ; // 直线的常数项 45 reg signed [15:0] curr_m ; //当前m值 46 reg [15:0] curr_dm ; //当前的m偏差量 47 reg [15:0] prev1_dm ; //上一次的m偏差量 48 reg [15:0] prev2_dm ; //上上一次m的偏差量 49 reg point_flag ; //找到当前最近点的标志位 50 51 wire signed [9:0] Xmin ; 52 wire signed [9:0] Xmax ; 53 wire signed [8:0] Ymin ; 54 wire signed [8:0] Ymax ; 55 56 assign dx = xe_in-xs_in; //得出X方向上的差值 57 assign dy = ye_in-ys_in; //得出Y方向上的差值 58 59 60 //求得所需的直线就在这么一个矩形框内,针对这个矩形框进行运算 61 assign Xmin = (xs_in<xe_in)? xs_in : xe_in ; 62 assign Xmax = (xs_in<xe_in)? xe_in : xs_in ; 63 assign Ymin = (ys_in<ye_in)? ys_in : ye_in ; 64 assign Ymax = (ys_in<ye_in)? ye_in : ys_in ; 65 66 //绝对值函数 67 function [15:0] abs (input signed[15:0] data1, input signed [15:0] data2 ); 68 abs = (data1>data2) ? (data1-data2):(data2-data1); 69 endfunction 70 71 //*********斜率的计算,并且扩大了2^6次方倍,并得出M值的2^6倍的大小****************** 72 always @ (posedge clock) 73 if(!in_en) 74 begin 75 //line_k <= 16'd0 ; 76 //line_m <= 16'd0 ; 77 end 78 else 79 begin 80 line_k <= (dy<<6)/(dx) ; 81 line_m <= (ye_in<<6) - line_k*xe_in ; 82 end 83 84 reg [9:0] x_cnt ; // X 坐标计数 85 reg [8:0] y_cnt ; // Y 坐标计数 86 //*********************矩形框X方向上计数********************************* 87 always @ (posedge clock ) 88 if(!reset) 89 begin 90 x_cnt <= 10'd0 ; 91 fini_flag <= 1'd0 ; 92 end 93 else if (in_en) //装载Xmin值 94 begin 95 x_cnt <= Xmin + 10'd1 ; 96 fini_flag <= 1'd0 ; 97 end 98 else if (x_cnt == Xmax) //矩形框扫描完毕 99 begin 100 fini_flag <= 1'd1 ; 101 end 102 else if ((y_cnt==Ymax)||(point_flag)) //列扫描完毕,x+1 103 begin 104 x_cnt <= x_cnt + 10'd1 ; 105 fini_flag <= 1'd0 ; 106 end 107 108 //********************矩形框Y方向上计数 ************** 109 always @ (posedge clock ) 110 if(!reset) 111 begin 112 y_cnt <= 9'd0 ; 113 end 114 else if (in_en) //装载Ymin值 115 begin 116 y_cnt <= Ymin - 9'd1 ; 117 end 118 else if ((y_cnt == Ymax)||(point_flag)) //列扫描完毕重新装载运算 119 begin 120 y_cnt <= Ymin ; 121 end 122 else begin 123 y_cnt <= y_cnt + 9'd1 ; 124 end 125 126 always @ (posedge clock ) 127 if ((!reset) || (in_en)) 128 begin 129 x_ou <= 10'd0 ; 130 y_ou <= 9'd0 ; 131 point_flag <= 1'd0 ; 132 prev1_dm <= 16'd0 ; 133 prev2_dm <= 16'd0 ; 134 curr_dm <= 16'd0 ; 135 end 136 else if (!fini_flag) 137 begin 138 //point_flag <= 1'd0 ; 139 curr_m <= (y_cnt<<6)-(x_cnt*line_k) ; 140 prev1_dm <= curr_dm ; 141 prev2_dm <= prev1_dm; 142 curr_dm <= abs(curr_m , line_m) ; 143 if((prev1_dm<curr_dm) &&(prev1_dm < prev2_dm)) //当前点在远离直线,以上一次的点有效 144 begin 145 point_flag <= 1'd1 ; //找到最近点,x,y跳转,结束x列的查找 146 prev1_dm <= 16'h00 ; 147 curr_dm <= 16'h00 ; 148 prev2_dm <= 16'h00 ; 149 x_ou <= x_cnt; 150 y_ou <= y_cnt - 9'd3 ; 151 end 152 else point_flag <= 1'd0 ; 153 end 154 else begin 155 point_flag <= 1'd0 ; 156 prev1_dm <= 16'h00 ; 157 curr_dm <= 16'h00 ; 158 prev2_dm <= 16'h00 ; 159 x_ou <= 10'd0 ; 160 y_ou <= 9'd0 ; 161 end 162 163 endmodule
附上测试tb
1 `timescale 1ns/1ps 2 3 4 module line_tb ; 5 6 reg clock ,reset ; 7 reg in_en ; 8 reg [9:0] xs_in ,xe_in ; 9 reg [8:0] ys_in ,ye_in ; 10 11 wire [9:0] x_ou ; 12 wire [8:0] y_ou ; 13 wire fini_flag ; 14 15 16 line U1_line( 17 .clock (clock), 18 .reset (reset), 19 .xs_in (xs_in), 20 .ys_in (ys_in), 21 .xe_in (xe_in), 22 .ye_in (ye_in), 23 .in_en (in_en), 24 25 .x_ou (x_ou), 26 .y_ou (y_ou), 27 .fini_flag (fini_flag) 28 ); 29 30 31 always #10 clock = ~clock ; 32 33 initial 34 begin 35 clock = 1'd0 ; reset =1'd0 ; in_en = 1'd0 ; 36 xs_in = 10'd0 ; xe_in = 10'd0 ; 37 ys_in = 9'd0 ; ye_in = 9'd0 ; 38 39 #40 reset = 1 ; in_en = 1 ; 40 xs_in = 100 ; xe_in = 200 ; 41 ys_in = 100 ; ye_in = 200 ; 42 #80 in_en = 0 ; 43 #80000 ; 44 #50000 ; 45 in_en = 1 ; 46 xs_in = 50 ; xe_in= 90 ; 47 ys_in = 120; ye_in= 20 ; 48 #80 in_en = 0 ; 49 #90000 $stop ; 50 51 end 52 53 endmodule
其实我听到这个project第一反应是软件中的两个for循环查找差值最小的点,再对应到HDL中想到的是generate,generate写到一半才发现输出怎么办,难道先让结果放到memory里面再慢慢取出来?觉得这么做是不是太繁琐了,于是叉掉重现写。
第二次想到的是用if else 实现软件中的for,写了n久都觉得这个逻辑关系很纠结,if else 都嵌套了。再加一层嵌套就不利于维护了,证明这个架构是不行的。
最后一想不是可以用计数器实现for吗,哎,是不是最近一直在看C,怎么把HDL设计思想和C 想混合了。对于大师来说我的这个思维历程很搞笑,对于菜鸟的我来说是第一次亲身感受到了软硬件代码设计思想的差异—— 以前只是书上说自己没有感触到