清霜一梦

line

0
阅读(1358)

小君童靴说头儿给了他一个project,实现给出屏幕上任意两个点,求出这两个点之间直线上的所有的点。觉得这个很好玩,就自己也写了一点code

/*
date : 2014/10/21
version : QuartusII 14.0 + DE1-SOC(cycloneV)
function: 输入两个点 Xmax = 1023  Ymax = 511 ,计算出中间点的坐标 

说明 : (1)1023=0x3ff  (10bit)    511 = 0x1ff (9bit)
       (2)直线方程: y=kx+m ;
       (3)第116行 装载Ymin值的时候-1, 要求输入Ymin不可以小于1:如
果这个地方不剪掉1,会导致输出有(Ymax-Ymin)个脉冲的延时
       (4)现象三的原因是 144 行的判定方法导致。判定当前点为合适点的
方式是第二次离直线最近,第一次prev2_dm 较大,第三次curr_dm也较大,116
行不减1就无法满足“远,近,远”的判定,从而一直扫到最后都找不到合适点。
       (5)本程序的计算方式是最直接的运算,计算量大,浪费硬件资源,更
好的算法等待大家的指点
       (6)由于计算输出延时了三个周期,如150行,所以最后三个点无法覆盖到。
*/


module line (
            clock ,
            reset ,
            xs_in ,        //输入的X 点的起始坐标
            ys_in ,        // 输入的Y 点的起始坐标 
            xe_in ,        //输入X点的终止坐标
            ye_in ,        //输入Y 点的终止坐标
            in_en ,        //当前输入信号有效标志    1:有效    0:无效
            
            x_ou,         //输出的X 点的坐标 
            y_ou,         // 输出的Y 点的坐标  
            fini_flag     //计算完成标志位
            );
  input         clock ,reset  ; 
  input         in_en ; 
  input [9:0]     xs_in  ,xe_in ;
  input [8:0]     ys_in  ,ye_in ; 
  
  output reg [9:0]    x_ou ; 
  output reg [8:0]    y_ou ; 
  output reg        fini_flag ; 
  
  wire signed [15:0] dx  ;     // X方向上的变化量
  wire signed [15:0] dy  ;     //Y方向上的变化量  
  reg signed  [15:0] line_k ;  // 用来存储线条的斜率        
  reg signed  [15:0]  line_m ;  // 直线的常数项 
  reg signed  [15:0]  curr_m ;     //当前m值
  reg         [15:0]  curr_dm ; //当前的m偏差量
  reg         [15:0]  prev1_dm ; //上一次的m偏差量
  reg         [15:0]  prev2_dm ; //上上一次m的偏差量
  reg                    point_flag ; //找到当前最近点的标志位    
  
  wire signed  [9:0] Xmin ; 
  wire signed  [9:0] Xmax ; 
  wire signed  [8:0] Ymin ; 
  wire signed  [8:0] Ymax ; 

  assign dx = xe_in-xs_in;  //得出X方向上的差值
  assign dy = ye_in-ys_in;  //得出Y方向上的差值
  
  
  //求得所需的直线就在这么一个矩形框内,针对这个矩形框进行运算
  assign Xmin = (xs_in<xe_in)? xs_in : xe_in ;
  assign Xmax = (xs_in<xe_in)? xe_in : xs_in ;
  assign Ymin = (ys_in<ye_in)? ys_in : ye_in ;
  assign Ymax = (ys_in<ye_in)? ye_in : ys_in ;
  
  //绝对值函数
  function [15:0] abs (input signed[15:0] data1,  input signed [15:0] data2 );
        abs = (data1>data2) ? (data1-data2):(data2-data1);
  endfunction 
        
//*********斜率的计算,并且扩大了2^6次方倍,并得出M值的2^6倍的大小******************  
  always @ (posedge clock)
        if(!in_en)
            begin 
                    //line_k <= 16'd0 ;
                    //line_m <= 16'd0 ;
            end 
        else 
            begin 
                    line_k <= (dy<<6)/(dx) ; 
                    line_m <= (ye_in<<6) - line_k*xe_in ; 
            end 
            
reg  [9:0] x_cnt ;        // X 坐标计数
reg  [8:0] y_cnt ;        // Y 坐标计数
//*********************矩形框X方向上计数*********************************    
always @ (posedge clock )
    if(!reset)             
            begin 
                    x_cnt     <= 10'd0 ; 
                    fini_flag <= 1'd0  ; 
            end 
    else if (in_en) //装载Xmin值
            begin 
                    x_cnt     <= Xmin + 10'd1 ;
                    fini_flag <= 1'd0  ; 
            end 
    else if (x_cnt == Xmax) //矩形框扫描完毕   
            begin 
                    fini_flag <= 1'd1  ;
            end 
    else if ((y_cnt==Ymax)||(point_flag))  //列扫描完毕,x+1 
            begin 
                    x_cnt     <= x_cnt + 10'd1 ; 
                    fini_flag <= 1'd0 ; 
            end 

//********************矩形框Y方向上计数 **************            
always @ (posedge clock )
    if(!reset)             
            begin 
                    y_cnt   <= 9'd0 ; 
            end 
    else if (in_en)  //装载Ymin值
            begin 
                    y_cnt   <= Ymin - 9'd1 ;
            end 
    else if ((y_cnt == Ymax)||(point_flag)) //列扫描完毕重新装载运算     
            begin 
                    y_cnt     <= Ymin ;
            end 
    else     begin 
                    y_cnt   <= y_cnt + 9'd1 ; 
            end 

  always @ (posedge clock )
    if ((!reset) || (in_en))  
            begin 
                    x_ou       <= 10'd0 ; 
                    y_ou       <= 9'd0  ; 
                    point_flag <= 1'd0  ;
                    prev1_dm   <= 16'd0 ;
                    prev2_dm   <= 16'd0 ;
                    curr_dm    <= 16'd0 ;  
            end 
    else if (!fini_flag)
            begin 
                    //point_flag <= 1'd0 ;
                    curr_m  <= (y_cnt<<6)-(x_cnt*line_k) ;
                    prev1_dm <= curr_dm ;
                    prev2_dm <= prev1_dm;
                    curr_dm <= abs(curr_m , line_m) ;
                    if((prev1_dm<curr_dm) &&(prev1_dm < prev2_dm)) //当前点在远离直线,以上一次的点有效
                            begin
                                    point_flag <= 1'd1 ; //找到最近点,x,y跳转,结束x列的查找
                                    prev1_dm   <= 16'h00 ;
                                    curr_dm    <= 16'h00 ;
                                    prev2_dm   <= 16'h00 ;
                                    x_ou <= x_cnt; 
                                    y_ou <= y_cnt - 9'd3 ;
                            end
                    else     point_flag <= 1'd0 ;
            end 
    else    begin
                    point_flag <= 1'd0 ;
                    prev1_dm   <= 16'h00 ;
                    curr_dm    <= 16'h00 ;
                    prev2_dm   <= 16'h00 ;
                    x_ou       <= 10'd0 ; 
                    y_ou       <= 9'd0  ; 
            end 

endmodule

附上测试tb

`timescale 1ns/1ps


module line_tb ;

reg clock ,reset ; 
reg in_en ;
reg [9:0] xs_in ,xe_in ;
reg [8:0] ys_in ,ye_in ; 

wire [9:0] x_ou ; 
wire [8:0] y_ou ; 
wire       fini_flag ;


line U1_line(
            .clock (clock),
            .reset (reset),
            .xs_in (xs_in),        
            .ys_in (ys_in),         
            .xe_in (xe_in),        
            .ye_in (ye_in),        
            .in_en (in_en),       
            
            .x_ou (x_ou),        
            .y_ou (y_ou),        
            .fini_flag (fini_flag)    
            );


always  #10 clock = ~clock ; 

initial 
    begin
            clock = 1'd0 ; reset =1'd0 ; in_en = 1'd0 ; 
            xs_in = 10'd0 ; xe_in = 10'd0 ; 
            ys_in = 9'd0  ; ye_in = 9'd0 ; 
                
            #40 reset = 1 ; in_en = 1 ; 
                xs_in = 100 ; xe_in = 200 ; 
                ys_in = 100 ; ye_in = 200 ; 
            #80 in_en = 0 ; 
            #80000 ;
            #50000 ;
            in_en = 1 ; 
            xs_in = 50 ; xe_in= 90 ; 
            ys_in = 120; ye_in= 20 ; 
            #80 in_en = 0 ; 
            #90000  $stop ; 
                
    end 

endmodule

其实我听到这个project第一反应是软件中的两个for循环查找差值最小的点,再对应到HDL中想到的是generate,generate写到一半才发现输出怎么办,难道先让结果放到memory里面再慢慢取出来?觉得这么做是不是太繁琐了,于是叉掉重现写。

第二次想到的是用if  else 实现软件中的for,写了n久都觉得这个逻辑关系很纠结,if else 都嵌套了。再加一层嵌套就不利于维护了,证明这个架构是不行的。

最后一想不是可以用计数器实现for吗,哎,是不是最近一直在看C,怎么把HDL设计思想和C 想混合了。对于大师来说我的这个思维历程很搞笑,对于菜鸟的我来说是第一次亲身感受到了软硬件代码设计思想的差异—— 以前只是书上说自己没有感触到