7.单次迭代运算单元(cordic_ir_unit) 经过预处理后的绝对值输出可以直接送入Cordic处理核进行 Cordic运算,Cordic运算实际上是n次迭代运算的过程。我们首先来看 一下单次迭代运算的设计。 前面也提到,我们的目标是使Y趋近于0。Y的符号位决定了旋转的 方向,也就是迭代器的方向。根据迭代公式,我们需要将上一流水线 的输出X(k),Y(k),Z(k)作为本次迭代工作的输入,根据4.2.6节的迭代 公式计算出新的旋转后的坐标X(k+1),Y(k+1),并查找当前旋转角度加 上当前角度值Z(k)作为输出Z(k+1)。 迭代器的核心部分在于移位和加法运算。以下的电路(见图7- 30)展示了第k次迭代的过程:
可见,每一次迭代需要1次取符号、1次查表、2次移位和3次乘加 运算,乘法也是简单的符号位扩展运算,运算延迟为1个时钟。 8.Cordic处理核单元(cordic_core) 将单次迭代单元迭代n次即可完成一次cordic运算。这里为了提高 处理精度,将输入数据扩展4位小数位。设计框图如图7-31所示。
在图7-3中,假定初始输入相位为0,即Z(0)=0,需要注意的是输 出并没有Y(n+1),这是由于在旋转之后Y(n+1)=0。 9.coedic后续处理模块(cordic_post) 如前所述,后续处理模块需实现坐标象限还原,很明显,这里的 象限指的是角度象限。我们已经在预处理单元中对输入坐标的象限进 行了保存,在此模块需要对象限位置进行恢复。 首先需要明确的一点是,由于处理核需要n个时钟来完成迭代运 算,预处理的象限信息需首先进行缓存n-1个时钟与处理结果进行对 齐。 在象限还原时,采用预处理的逆运算:
由归一化系数算出 ,π,π的归一化系数,依次进行转换即可。 后续处理模块2同时还需完成增益补偿,这个在前面也讨论过,移位近 似法即可实现要求。 其中,象限变换电路如图7-31所示。
由图7-31和图7-32可知,本模块的计算开销为3个时钟。 10.顶层模块(cordic_r2p) 顶层模块只需依次调用cordic_pre,cordic_core,cordic_post即 可。用quartus 13.0综合后的RTL视图如图7-33所示。
11.资源占用情况 我们同样用quartus 13.0来对所设计的系统进行资源评估,如图 7-34所示。
由图7-34可见,对于16位,迭代次数为15次,保留4位小数的 cordic坐标转换系统,仅仅消耗1288个查找表和1086个寄存器,对于 资源的消耗是非常少的,也达到了我们预期的设计目的。 到这里为止,我们已经把Sobel算子的核心算法部分介绍完毕了, 将Soble模板的X和Y方向上的计算结果接入Cordic坐标转换内核的输 入,即可得到Sobel算子的模值和方向数据流。通过简单的越界判断, 即可得到最终的Sobel运算结果。
7.3.4 Verilog代码设计
本节将列出Sobel模板计算电路和Cordic坐标转换电路的Verilog 示例代码。 1.Sobel模板计算 // 9个窗口寄存器
reg [DW-1:0]Sobel[0:3-1];
reg [DW-1:0]Sobel_r[0:3-1];
reg [DW-1:0]Sobel_r2[0:3-1];
//中间寄存器
reg [DW+4-1:0]Sobel_Result_Temp[0:3];
reg [DW+3-1:0]Sobel_Temp[0:12];
//x和y方向计算结果寄存器
reg [21-1:0]Sobel_Result_X;
reg [21-1:0]Sobel_Result_Y;//
//x方向Sobel模板计算
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
for (j = 0; j <= 6; j = j + 1)
Sobel_Temp[j]<= {DW+3{1'b0}};
Sobel_Result_Temp[0]<= {DW+4{1'b0}};
end
else
begin
if (din_valid_r[1]== 1'b1)
begin
Sobel_Temp[0]<= #1 ({2'b00, Sobel[1],1'b0});
Sobel_Temp[1]<= #1 ({3'b000,Sobel[0]}) +
({3'b000,Sobel[2]});
Sobel_Temp[2]<= #1 ({2'b00,
Sobel_r2[1],1'b0});Sobel_Temp[3]<=#1({3'b000,Sobel_r2[0]})+
({3'b000,Sobel_r2[2]});
end
if (din_valid_r[2]== 1'b1)
begin
Sobel_Temp[4]<= #1 Sobel_Temp[0]+
Sobel_Temp[1];
Sobel_Temp[5]<= #1 Sobel_Temp[2]+
Sobel_Temp[3];
end
if (din_valid_r[3]== 1'b1)
Sobel_Result_Temp[0]<= ({1'b0,Sobel_Temp[4]})
- ({1'b0,
Sobel_Temp[5]});
end
end
//y方向Sobel模板计算
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
for (k = 7; k <= 12; k = k + 1)
Sobel_Temp[k]<= {DW+3{1'b0}};
Sobel_Result_Temp[1]<= {DW+4{1'b0}};
end
else
beginif (din_valid_r[1]== 1'b1)
begin
Sobel_Temp[7]<=#1({3'b000,Sobel[0]})+
({3'b000,Sobel_r2[0]});
Sobel_Temp[8] <= #1
({2'b00,Sobel_r[0],1'b0});
Sobel_Temp[9]<=#1({3'b000,Sobel[2]})+
({3'b000,Sobel_r2[2]});
Sobel_Temp[10]<= #1
({2'b00,Sobel_r[2],1'b0});
end
if (din_valid_r[2]== 1'b1)
begin
Sobel_Temp[11]<= #1 Sobel_Temp[7]+
Sobel_Temp[8];
Sobel_Temp[12]<= #1 Sobel_Temp[9]+
Sobel_Temp[10];
end
if (din_valid_r[3]== 1'b1)
Sobel_Result_Temp[1]<=
({1'b0,Sobel_Temp[11]}) - ({1'b0,
Sobel_Temp[12]});
end
end
//buffer result for 1 clk and extend sign bits
always @(posedge clk)
beginif (rst_all == 1'b1)
begin
Sobel_Result_Temp[2]<= {DW+4{1'b0}};
Sobel_Result_Temp[3]<= {DW+4{1'b0}};
Sobel_Result_X <= 21'b0;
Sobel_Result_Y <= 21'b0;
end
else
begin
if (din_valid_r[4]== 1'b1)
begin
Sobel_Result_Temp[2]<=
Sobel_Result_Temp[0];//x sobel result
Sobel_Result_Temp[3]<=
Sobel_Result_Temp[1];//y sobel result
end
if (din_valid_r[5]== 1'b1)
begin
if(Sobel_Result_Temp[2][DW+3]== 1'b1)//x<0
Sobel_Result_X <= {{21-DW-
4{1'b1}},Sobel_Result_Temp[2]};
else
Sobel_Result_X <= {{21-DW-
4{1'b0}},Sobel_Result_Temp[2]};
if(Sobel_Result_Temp[3][DW+3]== 1'b1)//y<0
Sobel_Result_Y <= {{21-DW-
4{1'b1}},Sobel_Result_Temp[3]};else
Sobel_Result_Y <= {{21-DW-
4{1'b0}},Sobel_Result_Temp[3]};
end
end
end
//得到9个窗口缓存
always @(*) Sobel[0]<= din;
always @(*) Sobel[1]<= line_dout[0];
always @(*) Sobel[2]<= line_dout[1];
generate
begin : sobel_buf
genvar i;
for (i = 0; i <= KSZ - 1; i = i + 1)
begin : sobel_buf_inst
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
Sobel_r[i] <= {DW{1'b0}};
Sobel_r2[i]<= {DW{1'b0}};
end
else
begin
Sobel_r[i]<= Sobel[i];
Sobel_r2[i]<= Sobel_r[i];
endend
end
end
endgenerate
2.Cordic_pre模块
module cordic_pre(
clk, //同步时钟
rst_n, //异步复位
din_valid, //输入有效信号
din_x, //输入x
din_y, //输入y
dout_x, //输出x
dout_y, //输出y
dout_valid, //输出有效
dout_info //输出象限信息
);
parameter DW = 16;
localparam latency = 2; //计算开销2个时钟
input clk;
input rst_n;
input din_valid;
input [DW-1:0]din_x;
input [DW-1:0]din_y;
output dout_valid;
output reg [DW-1:0]dout_x;
output reg [DW-1:0]dout_y;
output reg [3-1:0]dout_info;reg [latency-1:0]din_valid_r;
//绝对值寄存器
reg [DW-1:0]x_abs;
reg [DW-1:0]y_abs;
//交换中间结果
wire [DW-1:0]x_swap;
wire [DW-1:0]y_swap;
/符号寄存器
reg x_sign_reg;
reg y_sign_reg;
wire swap;
//计算绝对值函数
function [DW-1:0]abs;
input [DW-1:0]data_in;
if(data_in[DW-1]== 1'b1) //输入<0
abs = 1'b1+(~data_in[DW-1:0]); //取反码+1
else
abs = data_in[DW-1:0]; //正数为原码
endfunction
//缓存输入有效信号
always @(posedge clk or negedge rst_n )
begin
begin
if(~rst_n)
begin
din_valid_r <= {latency{1'b0}};
endelse
begin
din_valid_r <= {din_valid_r[latency-
2:0],din_valid};
end
end
end
//计算输入绝对值和符号位
always @(posedge clk or negedge rst_n)
begin: Step1
begin
if(~rst_n)
begin
x_abs <= {DW{1'b0}};
x_sign_reg <= 1'b0;
y_abs <= {DW{1'b0}};
y_sign_reg <= 1'b0;
end
else
begin
if (din_valid == 1'b1)
begin
x_abs <= #1(abs(din_x));
x_sign_reg <= din_x[DW-1];
y_abs <= #1(abs(din_y));
y_sign_reg <= din_y[DW-1];
endend
end
end
//得到象限信号
assign x_swap = (y_abs > x_abs)?y_abs:x_abs;
assign y_swap = (y_abs > x_abs)?x_abs:y_abs;
assign swap = (y_abs > x_abs)?1'b1:1'b0;
//驱动输出信号
always @(posedge clk or negedge rst_n)
begin: Step2
begin
if(~rst_n)
begin
dout_x <= {DW{1'b0}};
dout_x <= {DW{1'b0}};
dout_info <= 3'b0;
end
else
begin
if (din_valid_r[0]== 1'b1)
begin
dout_x <= #1 x_swap;
dout_y <= #1 y_swap;
dout_info <= #1
{y_sign_reg,x_sign_reg,swap};
end
endend
end
assign dout_valid = din_valid_r[latency-1];
endmodule
3.Cordic_ir_unit模块
module cordic_ir_unit(
clk,
rst_n,
din_valid,
din_x,
din_y,
din_z,
dout_valid,
dout_x,
dout_y,
dout_z
);
parameter DW = 16;
parameter PIPE_ID = 1; //当前迭代ID
localparam DW_NOR = 20; //归一化位宽
localparam IR_NUM = 15; //总迭代次数
localparam latency = 1; //输出lantency
input clk;
input rst_n;
input din_valid;
input signed [DW-1:0]din_x;
input signed [DW-1:0]din_y;input [DW_NOR-1:0]din_z;
output reg [DW-1:0]dout_x;
output reg [DW-1:0]dout_y;
output reg [DW_NOR-1:0]dout_z;
output dout_valid;
wire y_is_neg;
wire y_is_pos;
//本次旋转后x y z的变化值
wire signed [DW-1:0]delta_x;
wire signed [DW-1:0]delta_y;
wire signed [DW_NOR-1:0]delta_z;
wire signed[DW-1:0]dout_temp_x;
wire signed[DW-1:0]dout_temp_y;
wire [DW_NOR-1:0]dout_temp_z;
reg din_valid_r;
//缓存输入数据和输入有效信号
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
begin
din_valid_r <= 1'b0;
end
else
begin
din_valid_r <= din_valid;
end
end//查找表
wire [DW_NOR-1:0]atan_lut[0:IR_NUM-1];
assign atan_lut[0] = 20'h20000;
assign atan_lut[1] = 20'h12E40;
assign atan_lut[2] = 20'h09FB4;
assign atan_lut[3] = 20'h05111;
assign atan_lut[4] = 20'h028B1;
assign atan_lut[5] = 20'h0145D;
assign atan_lut[6] = 20'h00A2F;
assign atan_lut[7] = 20'h00518;
assign atan_lut[8] = 20'h0028C;
assign atan_lut[9] = 20'h00146;
assign atan_lut[10]= 20'h000A3;
assign atan_lut[11]= 20'h00051;
assign atan_lut[12]= 20'h00029;
assign atan_lut[13]= 20'h00014;
assign atan_lut[14]= 20'h0000A;
assign atan_lut[15]= 20'h00005;
assign atan_lut[16]= 20'h00003;
assign atan_lut[17]= 20'h00001;
//输入y符号确定
assign y_is_neg = din_y[DW - 1]; //indicate if Y<0
assign y_is_pos = (~din_y[DW - 1]); //indicate if Y>0
assign delta_z = atan_lut[PIPE_ID];
//迭代公式计算
generate
if(PIPE_ID == 0) //PIPE_ID == 0 no shift operationbegin :shift_none //旋转45°
assign delta_x = (din_valid==1'b1)?din_y:
{DW{1'b0}};
assign delta_y = (din_valid==1'b1)?din_x:
{DW{1'b0}};
end
endgenerate
//旋转其他角度,右移k位得到当前旋转后变化值
generate
if(PIPE_ID != 0)
begin :shift_right
wire signed [DW-1:0]delta_x_temp;
wire signed [DW-1:0]delta_y_temp;
assign delta_x_temp = (din_valid==1'b1)?din_y:
{DW{1'b0}};
assign delta_y_temp = (din_valid==1'b1)?din_x:
{DW{1'b0}};
//带符号位的右移运算
assign delta_x = (din_y[DW-1]==1'b1)?
{{PIPE_ID{1'b1}},delta_x_temp[DW-
1:PIPE_ID]}:
{{PIPE_ID{1'b0}},delta_x_temp[DW-
1:PIPE_ID]};
assign delta_y = (din_x[DW-1]==1'b1)?
{{PIPE_ID{1'b1}},delta_y_temp[DW-
1:PIPE_ID]}:{{PIPE_ID{1'b0}},delta_y_temp[DW-
1:PIPE_ID]};
end
endgenerate
//Iterative expression
assign dout_temp_x = (y_is_pos)?(din_x+delta_x):(din_xdelta_x);
//if(Yi>0) X(n+1) = X(n) + dY else X(n+1) = X(n) - dY
assign dout_temp_y = (y_is_neg)?(din_y+delta_y):(din_ydelta_y);
//if(Yi>0) X(n+1) = Y(n) + dX else X(n+1) = Y(n) - dX
assign dout_temp_z = (y_is_pos)?(din_z+delta_z):(din_zdelta_z);
//if(Yi>0) Z(n+1) = Z(n) + dZ else Z(n+1) = X(n) - dY
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
begin
dout_x <= {DW{1'b0}};
dout_y <= {DW{1'b0}};
dout_z <= {DW_NOR{1'b0}};
end
else
begin
if(din_valid == 1'b1)
begin
dout_x <= dout_temp_x ;dout_y <= dout_temp_y ;
dout_z <= dout_temp_z ;
end
end
end
assign dout_valid = din_valid_r;
endmodule
4.Cordic_core模块
module cordic_core(
clk,
rst_n,
din_valid,
din_x,
din_y,
din_z,
dout_valid,
dout_x,
dout_z
);
parameter PIPELINE = 15; //总迭代次数
parameter DW = 16;
parameter DW_FRAC = 4; //扩展小数位宽
parameter DW_NOR = 20; //归一化位宽DW
input clk;
input din_valid;
input rst_n;
input [DW-1:0]din_x;input [DW-1:0]din_y;
input [DW_NOR-1:0]din_z;
output [DW+DW_FRAC-1:0]dout_x;
output [DW_NOR-1:0]dout_z;
output dout_valid;
wire [DW+DW_FRAC-1:0]din_x_frac[PIPELINE:0];
wire [DW+DW_FRAC-1:0]din_y_frac[PIPELINE:0];
wire [DW_NOR-1:0]din_z_temp[PIPELINE:0];
wire dout_valid_temp[PIPELINE:0];
//将输入数据接入迭代器 并扩展小数位
assign din_x_frac[0][DW + DW_FRAC - 1:DW_FRAC]= din_x;
assign din_x_frac[0][DW_FRAC - 1:0]= {DW_FRAC{1'b0}};
assign din_y_frac[0][DW + DW_FRAC - 1:DW_FRAC]= din_y;
assign din_y_frac[0][DW_FRAC - 1:0]= {DW_FRAC{1'b0}};
assign din_z_temp[0]= din_z;
assign dout_valid_temp[0]= din_valid;
//例化PIPELINE个迭代器单元
//采用菊花链结构
generate
begin : gen_iteration
genvar n;
for (n = 1; n <= PIPELINE; n = n + 1)
begin : gen_pipeline
cordic_ir_unit unit(
.clk(clk),
.rst_n(rst_n),
.din_valid(dout_valid_temp[n-1]),.din_x(din_x_frac[n-1]),//接上一个单元输
出
.din_y(din_y_frac[n-1]),
.din_z(din_z_temp[n-1]),
.dout_valid(dout_valid_temp[n]),
.dout_x(din_x_frac[n]),//接下个单元输入
.dout_y(din_y_frac[n]),
.dout_z(din_z_temp[n])
);
defparam unit.DW = DW + DW_FRAC ;
defparam unit.PIPE_ID = n - 1;
end
end
endgenerate
//output iteration result
assign dout_x = din_x_frac[PIPELINE];
assign dout_z = din_z_temp[PIPELINE];
assign dout_valid = dout_valid_temp[PIPELINE];
endmodule
5.Cordic_post模块
module cordic_post(
clk,
rst_n,
din_valid,
din_x,
din_z,
din_info,//输入象限信息dout_valid,
dout_x,
dout_z
);
parameter DW = 16;
parameter DW_FRAC = 4;
parameter DW_NOR = 20;
parameter PIPELINE = 15;
localparam lantency_pre = 2;
localparam lantency_core = PIPELINE;
localparam latency = 3;
//由归一化系数计算出来的 ,π,π2
localparam const_half_pi = 20'h40000;
localparam const_pi = 20'h80000;
localparam const_double_pi = 20'h00000;
input clk;
input rst_n;
input din_valid;
input [2:0]din_info;//包含8象限信息
input [DW+DW_FRAC-1:0]din_x;
input [DW_NOR-1:0]din_z;
output [DW+DW_FRAC-1:0]dout_x;
output reg[DW_NOR-1:0]dout_z;
output dout_valid;
integer n;
reg [DW+DW_FRAC-1:0]gain_temp[0:3];wire [DW_NOR-1:0]angle_temp;
wire [DW_NOR-1:0]angle_valid;
wire [DW_NOR-1:0]angle_swap;
reg [2:0]din_info_r[lantency_core+latency-1:0];
reg [latency-1:0]din_valid_r;
reg [DW_NOR-1:0]angle_swap_r[0:latency-1];
wire [DW_NOR-1:0]angle_temp_x;
wire [DW_NOR-1:0]angle_temp_y;
//旋转增益补偿 :×0.60727
//0.60727=1/2+1/8-1/64-1/512-(1/2+1/8-1/64-
1/512)*1/4096
//err 0.0034%
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
begin
gain_temp[0]<= {DW+DW_FRAC{1'b0}};
gain_temp[1]<= {DW+DW_FRAC{1'b0}};
gain_temp[2]<= {DW+DW_FRAC{1'b0}};
gain_temp[3]<= {DW+DW_FRAC{1'b0}};
end
else
begin
if(din_valid == 1'b1)
begin
gain_temp[0]<= {1'b0,din_x[DW+DW_FRAC-1:1]} +
{3'b0,din_x[DW+DW_FRAC-1:3]};gain_temp[1]<= {6'b0,din_x[DW+DW_FRAC-1:6]} +
{9'b0,din_x[DW+DW_FRAC-1:9]};
end
if(din_valid_r[0]== 1'b1)
begin
gain_temp[2]<= gain_temp[0]- gain_temp[1];
end
if(din_valid_r[1]== 1'b1)
begin
gain_temp[3]<= gain_temp[2]-
{12'b0,gain_temp[2][DW+DW_FRAC-1:12]};
end
end
end
assign dout_x = gain_temp[3];
//计算角度
assign angle_valid = din_valid?din_z:({DW_NOR{1'b0}});
assign angle_temp = (angle_valid[DW_NOR-1]==1'b1)?
({DW_NOR{1'b0}}):angle_valid;
//1/8象限计算
assign angle_swap = (din_info_r[lantency_core-1]
[0]==1'b1)?
(const_half_pi -
angle_temp):angle_temp;
// 1/4象限计算
always @(posedge clk)
begindin_valid_r <= {din_valid_r[latency-
2:0],din_valid};
angle_swap_r[0]<= angle_swap;
angle_swap_r[1]<= angle_temp_x;
end
always @(posedge clk)
begin
din_info_r[0] <= din_info;
for( n = 1; n < lantency_core+latency; n = n+1 )
din_info_r[n]<= din_info_r[n-1];
end
assign angle_temp_x = (din_info_r[lantency_core]
[1]==1'b1)?
(const_pi -
angle_swap_r[0]):angle_swap_r[0];
assign angle_temp_y = (din_info_r[lantency_core+1]
[2]==1'b1)?
(const_double_pi -
angle_swap_r[1]):angle_swap_r[1];
always @(posedge clk)
begin
dout_z <= angle_temp_y;
end
assign dout_valid = din_valid_r[latency-1];
endmodule
6.Cordic_r2p模块
module cordic_r2p(clk,
rst_n,
din_valid, //输入有效
din_x,
din_y,
dout_valid, //输出有效
dout_angle, //输出角度
dout_radians //输出模值
);
parameter DW = 16; //输入数据位宽
parameter DW_NOR = 20; //归一化宽度
parameter PipeLength = 15; //总迭代次数
parameter DW_FRAC = 4; //扩展小数位
input clk;
input rst_n;
input [DW-1:0]din_x;
input [DW-1:0]din_y; //input y
input din_valid;
output dout_valid;
output[DW+DW_FRAC-
1:0]dout_radians;//16*sqrt(din_x*din_x+din_y*din_y)
output [DW_NOR-1:0] dout_angle; // arctan(y/x) 归一化范
围 0~2^20
//也即dout_angle
wire signed [DW-1:0]pre_dout_x;
wire signed [DW-1:0]pre_dout_y;wire [ 3-1:0]pre_dout_info;
wire pre_dout_valid;
wire unsigned [DW+DW_FRAC-1:0]core_dout_x;
wire [DW_NOR-1:0]core_dout_z;
wire core_dout_valid;
//预处理
cordic_pre pre_ins(
.clk(clk),
.rst_n(rst_n),
.din_valid(din_valid),
.din_x(din_x),
.din_y(din_y),
.dout_x(pre_dout_x),
.dout_y(pre_dout_y),
.dout_valid(pre_dout_valid),
.dout_info(pre_dout_info)
);
defparam pre_ins.DW = DW;
//cordic 内核处理
cordic_core core_ins(
.clk(clk),
.rst_n(rst_n),
.din_valid(pre_dout_valid),
.din_x(pre_dout_x),
.din_y(pre_dout_y),
.din_z(20'b0),
.dout_valid(core_dout_valid),.dout_x(core_dout_x),
.dout_z(core_dout_z)
);
defparam core_ins.PIPELINE = PipeLength;
defparam core_ins.DW = DW;
defparam core_ins.DW_FRAC = DW_FRAC;
defparam core_ins.DW_NOR = DW_NOR;
//后期处理
cordic_post post_ins(
.clk(clk),
.rst_n(rst_n),
.din_valid(core_dout_valid),
.din_x(core_dout_x),
.din_z(core_dout_z),
.din_info(pre_dout_info),
.dout_valid(dout_valid),
.dout_x(dout_radians),
.dout_z(dout_angle)
);
defparam core_ins.DW = DW;
defparam core_ins.DW_FRAC = DW_FRAC;
defparam core_ins.DW_NOR = DW_NOR;
defparam core_ins.PIPELINE = PipeLength;