#!/usr/bin/perl # # Jeff Mock # 2030 Gough # San Francisco, CA 94109 # jeff@mock.com # (c) 2004 # # # $Id: mkdlpf_delay 190 2004-08-10 00:51:03Z jeff $ # use Getopt::Long; use Math::Trig; use Math::BigInt; use POSIX; $opt_width = 18; # datapath width $opt_n = 32; $opt_delay = 0; # delay length $opt_vdelay = 0; # max variable delay length $opt_dir = "."; $opt_odir = "."; $opt_prefix = "jlpf"; $opt_norun = 0; $opt_imp = "virtex2"; $opt_cm3 = 0; $opt_ce = 0; $opt_xname = ""; $0 =~ /(.*)\/.*/; $opt_dir = $1 eq "" ? "." : $1; %opts = ( 'width=o' => \$opt_width, 'n=o' => \$opt_n, 'delay=o' => \$opt_delay, 'vdelay=o' => \$opt_vdelay, 'dir=s' => \$opt_dir, 'odir=s' => \$opt_odir, 'prefix=s' => \$opt_prefix, 'norun' => \$opt_norun, 'imp=s' => \$opt_imp, 'cm3' => \$opt_cm3, 'ce' => \$opt_ce, 'xname=s' => \$opt_xname, ); if (!GetOptions(%opts)) { print STDERR " Generate pieces of verilog for a decimating LPF mkdlpf_delay [options] [--n=n] Size of LPF [--width=n] Datapath width of LPF ($opt_width) [--delay=n] Pipeline delays to generate ($opt_delay) [--vdelay=n] Max variable delays to generate ($opt_vdelay) [--dir=s] Directory with other mkdlpf programs ($opt_dir) [--odir=s] Output directory for verilog ($opt_odir) [--prefix=s] Prefix module name with string ($opt_prefix) [--norun] Do not recurse and build sub-modules [--imp=s] Set target implementation ($opt_imp) [--ce] Add clock enable pin to interface [--xname=s] Extra suffix for name (and filename) \n"; exit 1; } $opt_xname = "_${opt_xname}" if $opt_xname; sub pcode { my $fd = shift; my $sp = shift; my $code = shift; $code =~ s/^.*?\n//m; $code =~ s/^ {$sp}//mg; $code =~ s/ *$//; print $fd $code; } sub log2 { my $v = shift; return int(log(2*$v-1)/log(2)); } our $bnum = 0; sub virtex2_bram { my $fd = shift; my $raddr = shift; my $waddr = shift; my $dout = shift; my $din = shift; my $awid = shift; my $dwid = shift; my %parts = ( # depth => bram address bits : # main data width of bram : # parity data width of bram : # name of single port part : # name of dual port part 3 => "9:32:4:RAMB16_S36_S36", 4 => "9:32:4:RAMB16_S36_S36", 5 => "9:32:4:RAMB16_S36_S36", 6 => "9:32:4:RAMB16_S36_S36", 7 => "9:32:4:RAMB16_S36_S36", 8 => "9:32:4:RAMB16_S36_S36", 9 => "9:32:4:RAMB16_S36_S36", 10 => "10:16:2:RAMB16_S18_S18", 11 => "11:8:1:RAMB16_S9_S9", 12 => "12:4:0:RAMB16_S4_S4", 13 => "13:2:0:RAMB16_S2_S2", 14 => "14:1:0:RAMB16_S1_S1", ); die "delay address width ($awid) is too big for virtex2" if ($awid > 14); die "delay address width ($awid) is too small for virtex2" if ($awid < 3); die "delay width ($dwid) is too small" if ($dwid < 1); my ($braddr, $mwid, $pwid, $brname) = split ':', $parts{$awid}; my $nparts = ceil($dwid/($mwid+$pwid)); # Make two tables for the width in each memory to use in # the main part and the parity part. Use the main parts # first and then go back and use parity parts. The bit # assignments are different though, first bit range is # main part of first memory, second section is parity of # first memory, and so on. # my @muwid = (); my @puwid = (); my $bits = $dwid; for my $npart (0 .. $nparts-1) { $muwid[$npart] = $bits; $muwid[$npart] = $mwid if $mwid < $bits; $bits -= $muwid[$npart]; } for my $npart (0 .. $nparts-1) { $puwid[$npart] = $bits; $puwid[$npart] = $pwid if $pwid < $bits; $bits -= $puwid[$npart]; } die "Botch: $bits bits left after width assignment" if $bits > 0; my $bitpos = 0; for my $npart (0 .. $nparts-1) { my $bname = sprintf("m%02d", $bnum++); my $ram_addrr = $raddr; $ram_addrr = sprintf("{ %d'b0, %s}", $braddr-$awid, $raddr) if $braddr > $awid; my $ram_addrw = $waddr; $ram_addrw = sprintf("{ %d'b0, %s}", $braddr-$awid, $waddr) if $braddr > $awid; my $ram_do = sprintf("%s[%d:%d]", $dout, $bitpos+$muwid[$npart]-1, $bitpos); my $ram_di = sprintf("%s[%d:%d]", $din, $bitpos+$muwid[$npart]-1, $bitpos); if ($mwid > $muwid[$npart]) { my $ew = $mwid - $muwid[$npart]-1; pcode($fd, 16, " wire [${ew}:0] dnc_${bname}; "); $ram_do = "{dnc_${bname}, ${ram_do}}"; $ram_di = sprintf("{%d\'b0, %s}", $ew+1, $ram_di); } $bitpos += $muwid[$npart]; my $ram_pdo = ""; my $ram_pdi = ""; if ($pwid > 0) { if ($puwid[$npart] > 0) { $ram_pdo = sprintf("%s[%d:%d]", $dout, $bitpos+$puwid[$npart]-1, $bitpos); $ram_pdi = sprintf("%s[%d:%d]", $din, $bitpos+$puwid[$npart]-1, $bitpos); if ($pwid > $puwid[$npart]) { my $ew = $pwid - $puwid[$npart]-1; pcode($fd, 24, " wire [${ew}:0] pnc_${bname}; "); $ram_pdo = "{pnc_${bname}, ${ram_pdo}}"; $ram_pdi = sprintf("{%d\'b0, %s}", $ew+1, $ram_pdi); } $bitpos += $puwid[$npart]; } else { my $ew = $pwid-1; pcode($fd, 20, " wire [${ew}:0] pnc_${bname}; "); $ram_pdo = "pnc_${bname}"; $ram_pdi = sprintf("%d\'b0", $ew+1); } } pcode($fd, 8, " ${brname} ${bname} ( .CLKA ( ck ), .CLKB ( ck ), .ADDRA ( ${ram_addrr} ), .ADDRB ( ${ram_addrw} ), // .DIA ( ), .DIB ( ${ram_di} ), .DOA ( ${ram_do} ), // .DOB ( ), "); pcode($fd, 8, " // .DIPA ( ), .DIPB ( ${ram_pdi} ), .DOPA ( ${ram_pdo} ), // .DOPB ( ), ") if $pwid>0; pcode($fd, 8, " .ENA ( ce ), .WEA ( 1'b0 ), .SSRA ( 1'b0 ), .ENB ( ce ), .WEB ( 1'b1 ), .SSRB ( 1'b0 ) ); "); } } sub delay_module { my $fd; my $fn = "${opt_odir}/${opt_prefix}${opt_xname}_delay_${opt_delay}.v"; my $ce_pin = $opt_ce ? "\n ce," : ""; my $ce_decl = $opt_ce ? "\n input ce;" : ""; if (-s $fn) { print " $fn exists, mkdlpf_delay not creating\n"; return 0; } print " Creating $fn\n"; open $fd, "> $fn" or die "mkdlpf_delay cannot create file $fn.\n $!"; pcode($fd, 8, " // ${opt_delay} pipeline delays of ${opt_width} bit word // // Generated by mkdlpf_delay // module ${opt_prefix}${opt_xname}_delay_${opt_delay} ( ck,${ce_pin} reset, a_re, a_im, x_re, x_im ); parameter width = ${opt_width}; input ck;${ce_decl} input reset; input [width-1:0] a_re; input [width-1:0] a_im; output [width-1:0] x_re; output [width-1:0] x_im; "); pcode($fd, 8, " wire ce; assign ce = 1'b1; ") if !$opt_ce; if ($opt_delay == 1) { pcode($fd, 12, " reg [width-1:0] x_re; reg [width-1:0] x_im; always @(posedge ck) begin if (ce) begin x_re <= a_re; x_im <= a_im; end end endmodule "); } elsif ($opt_delay == 2) { pcode($fd, 12, " reg [width-1:0] x_re; reg [width-1:0] x_re_del; reg [width-1:0] x_im; reg [width-1:0] x_im_del; always @(posedge ck) begin if (ce) begin x_re_del <= a_re; x_re <= x_re_del; x_im_del <= a_im; x_im <= x_im_del; end end endmodule "); } elsif (($opt_delay <= 129 && $opt_imp ne "behave") || $opt_delay <= 17 ) { my $delay = $opt_delay-1; my $inn = "a"; my $onn; my $cnt = 0; while ($delay > 0) { $cdelay = 16; $cdelay = $delay if $delay<16; $delay -= $cdelay; if ($delay > 0) { $onn = sprintf("x_%d", $cnt); } else { $onn = "x_p"; } pcode($fd, 12, " wire[width-1:0] ${onn}_re; wire[width-1:0] ${onn}_im; "); my $dv = $cdelay-1; my $dvm1 = $cdelay-2; my $a0 = ($dv & 1); my $a1 = (($dv>>1) & 1); my $a2 = (($dv>>2) & 1); my $a3 = (($dv>>3) & 1); for my $i (0 .. $opt_width-1) { if ($opt_imp eq "virtex2") { pcode($fd, 20, " SRL16E br_${cnt}_${i} ( .CLK ( ck ), .CE ( ce ), .D ( ${inn}_re[${i}] ), .A0 ( 1'b${a0} ), .A1 ( 1'b${a1} ), .A2 ( 1'b${a2} ), .A3 ( 1'b${a3} ), .Q ( ${onn}_re[${i}] ) ); SRL16E bi_${cnt}_${i} ( .CLK ( ck ), .CE ( ce ), .D ( ${inn}_im[${i}] ), .A0 ( 1'b${a0} ), .A1 ( 1'b${a1} ), .A2 ( 1'b${a2} ), .A3 ( 1'b${a3} ), .Q ( ${onn}_im[${i}] ) ); "); } else { pcode($fd, 20, " reg [${dv}:0] delr_${cnt}_${i}; reg [${dv}:0] deli_${cnt}_${i}; always @(posedge ck) begin if (ce) begin delr_${cnt}_${i} <= { delr_${cnt}_${i}\[${dvm1}:0], ${inn}_re[${i}] }; deli_${cnt}_${i} <= { deli_${cnt}_${i}\[${dvm1}:0], ${inn}_im[${i}] }; end end assign ${onn}_re[${i}] = delr_${cnt}_${i}\[${dv}]; assign ${onn}_im[${i}] = deli_${cnt}_${i}\[${dv}]; "); } }; $inn = $onn; $cnt++; } pcode($fd, 12, " reg [width-1:0] x_re; reg [width-1:0] x_im; always @(posedge ck) begin if (ce) begin x_re <= x_p_re; x_im <= x_p_im; end end endmodule "); } else { # This may fail is the delay is something close but # not equal to a power of 2 (like 255 or 257). # my $awid = log2($opt_delay); my $awidm1 = $awid-1; my $mwidm1 = 2 * $opt_width-1; my $mwid = 2 * $opt_width; my $wid = $opt_width; my $widm1 = $opt_width - 1; my $memsz = (1 << $awid) - 1; my $mdel = $opt_delay - 2; die "delay must be power of 2: ${opt_delay}" if $opt_delay != (1 << $awid); if ($opt_imp eq "virtex2") { pcode($fd, 16, " // Keep the read and write addresses separate to // avoid problem with symantics with different types // memories about when read and write happen to the // same address in the same cycle. // reg [width-1:0] x_re; reg [width-1:0] x_im; reg [${awidm1}:0] waddr; reg [${awidm1}:0] raddr; wire [${mwidm1}:0] mem_in; wire [${mwidm1}:0] memreg; assign mem_in = {a_re, a_im}; always @(posedge ck) begin if (reset) begin raddr <= ${awid}'d0; waddr <= ${awid}'d${mdel}; end else begin if (ce) begin waddr <= waddr + 1; raddr <= raddr + 1; end end end "); virtex2_bram($fd, "raddr", "waddr", "memreg", "mem_in", $awid, $mwid); pcode($fd, 16, " always @(posedge ck) begin if (ce) begin x_re <= memreg[${mwidm1}:${wid}]; x_im <= memreg[${widm1}:0]; end end endmodule "); } else { pcode($fd, 16, " // Keep the read and write addresses separate to // avoid problem with symantics with different types // memories about when read and write happen to the // same address in the same cycle. // reg [${awidm1}:0] waddr; reg [${awidm1}:0] raddr; reg [${mwidm1}:0] mem [0:${memsz}]; reg [${mwidm1}:0] memreg; reg [${widm1}:0] x_re; reg [${widm1}:0] x_im; always @(posedge ck) begin if (reset) begin raddr <= ${awid}'d0; waddr <= ${awid}'d${mdel}; end else begin if (ce) begin mem[waddr] <= {a_re, a_im}; memreg <= mem[raddr]; waddr <= waddr + 1; raddr <= raddr + 1; end end end always @(posedge ck) begin if (ce) begin x_re <= memreg[${mwidm1}:${wid}]; x_im <= memreg[${widm1}:0]; end end endmodule "); } } close $fd; } sub vdelay_module { my $fd; my $fn = "${opt_odir}/${opt_prefix}${opt_xname}_delay_${opt_vdelay}.v"; my $ce_pin = $opt_ce ? "\n ce," : ""; my $ce_decl = $opt_ce ? "\n input ce;" : ""; my $awid = log2($opt_vdelay); my $awidm1 = $awid-1; my $mwidm1 = 2 * $opt_width-1; my $mwid = 2 * $opt_width; my $wid = $opt_width; my $widm1 = $opt_width - 1; my $memsz = (1 << $awid) - 1; my $mdel = $opt_vdelay - 2; if (-s $fn) { print " $fn exists, mkdlpf_delay not creating\n"; return 0; } print " Creating $fn\n"; open $fd, "> $fn" or die "mkdlpf_delay cannot create file $fn.\n $!"; pcode($fd, 8, " // ${opt_vdelay} max variable delays of ${opt_width}-bits // // Generated by mkdlpf_delay // module ${opt_prefix}${opt_xname}_delay_${opt_vdelay} ( ck,${ce_pin} reset, dec, a_re, a_im, x_re, x_im ); parameter width = ${opt_width}; input ck;${ce_decl} input reset; input [${awidm1}:0] dec; input [width-1:0] a_re; input [width-1:0] a_im; output [width-1:0] x_re; output [width-1:0] x_im; "); pcode($fd, 8, " wire ce; assign ce = 1'b1; ") if !$opt_ce; if ($opt_imp eq "virtex2") { pcode($fd, 12, " // Keep the read and write addresses separate to // avoid problem with symantics with different types // memories about when read and write happen to the // same address in the same cycle. // reg [width-1:0] x_re; reg [width-1:0] x_im; reg [width-1:0] a_re_d1; reg [width-1:0] a_im_d1; reg dec2; reg [${awidm1}:0] waddr; reg [${awidm1}:0] raddr; wire [${mwidm1}:0] mem_in; wire [${mwidm1}:0] memreg; assign mem_in = {a_re, a_im}; always @(posedge ck) begin dec2 <= (dec == ${awid}'d2); if (ce) begin a_re_d1 <= a_re; a_im_d1 <= a_im; end if (reset) begin raddr <= ${awid}'d0; end else begin if (ce) begin raddr <= raddr + 1; waddr <= raddr + dec - ${awid}'d1; end end end "); virtex2_bram($fd, "raddr", "waddr", "memreg", "mem_in", $awid, $mwid); pcode($fd, 12, " always @(posedge ck) begin if (ce) begin x_re <= dec2 ? a_re_d1 : memreg[${mwidm1}:${wid}]; x_im <= dec2 ? a_im_d1 : memreg[${widm1}:0]; end end endmodule "); } else { pcode($fd, 12, " // Keep the read and write addresses separate to // avoid problem with symantics with different types // memories about when read and write happen to the // same address in the same cycle. // reg [${awidm1}:0] waddr; reg [${awidm1}:0] raddr; reg [${mwidm1}:0] mem [0:${memsz}]; reg [${mwidm1}:0] memreg; reg [width-1:0] x_re; reg [width-1:0] x_im; reg [width-1:0] a_re_d1; reg [width-1:0] a_im_d1; reg dec2; always @(posedge ck) begin dec2 <= (dec == ${awid}'d2); if (ce) begin a_re_d1 <= a_re; a_im_d1 <= a_im; end if (reset) begin raddr <= ${awid}'d0; end else begin if (ce) begin mem[waddr] <= {a_re, a_im}; memreg <= mem[raddr]; raddr <= raddr + ${awid}'d1; waddr <= raddr + dec - ${awid}'d1; end end end always @(posedge ck) begin if (ce) begin x_re <= dec2 ? a_re_d1 : memreg[${mwidm1}:${wid}]; x_im <= dec2 ? a_im_d1 : memreg[${widm1}:0]; end end endmodule "); } close $fd; } die "Must specify only one --delay=n or --vdelay=n" if $opt_delay && $opt_vdelay; delay_module() if $opt_delay; vdelay_module() if $opt_vdelay;