# This file is part of librefdatool. librefdatool is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright (C) 2013 Javier D. Garcia-Lasheras # import sys class snippets: # we are going to use dictionary-based string formatting def libraries(self): libString = ( 'library ieee;\n' 'use ieee.std_logic_1164.all;\n' 'use ieee.numeric_std.all;\n\n' ) return libString def entity(self, name, busX, busY): entityDict = { 'Name': name, 'MSBitX': busX - 1, 'MSBitY': busY - 1 } entityString = ( 'entity %(Name)s is\n' ' port (\n' ' clk : in std_logic;\n' ' rst : in std_logic;\n' ' sig_in : in signed(%(MSBitX)u downto 0);\n' ' sig_out : out signed(%(MSBitY)u downto 0)\n' ' );\n' 'end entity;\n' ) return entityString % entityDict def architectureHeader(self, name, structure): #TBD: include the appropriated realization diagram archHeadDict = { 'Name': name, 'Structure': structure } archHeadString = ( 'architecture %(Structure)s of %(Name)s is\n\n' '-- Architecture depends on selected realization\n' '-- TODO: this header should each different realization\n' '--\n' '-- example, transposed direct form for FIR:\n' '--\n' '-- z^-1 z^-1 z^-1 z^-1\n' '-- o-->--o-->--o-->- - - --o-->--o-->--o-->--o y[n]\n' '-- | | | | | | \n' '-- ^Bm ^Bm-1 ^Bm-2 ^B2 ^B1 ^B0\n' '-- | | | | | |\n' '-- x[n] o-->--o-->--o-->--o-->- - - --o-->--o-->--\n' '--\n' ) return archHeadString % archHeadDict def convertFixedPoint(self, nameX, pX, qX, nameY, pY, qY, overflow=False, language='vhdl'): # TBD: overflow handling # The syntax is not very handy when pX, qX, pY, qY, qDiff or pDiff are equal to 1 => (i downto i)??? pDiff = pY - pX qDiff = qY - qX # INTEGER PART if pDiff == 0: # Destination integer part is == than the input one signExt = '' MSBpY = pY + qY - 1 LSBpY = qY MSBpX = pX + qX - 1 LSBpX = qX elif pDiff > 0: signExt = '%(nameY)s({!s} downto {!s}) <= (others => %(nameX)s({!s}));\n'.format(pY+qY-1, pY+qY-pDiff, pX+qX-1) MSBpY = pY + qY - 1 - pDiff LSBpY = qY MSBpX = pX + qX - 1 LSBpX = qX # Destination integer part is > than the input one # sign extension in the extra Y bits!! # Now the rest of the significant bits else: # pdiff < 0 signExt = '' MSBpY = pY + qY - 1 LSBpY = qY MSBpX = pX + qX - 1 + pDiff LSBpX = qX # Destination integer part is < than the input one # We should be carefull with overflow: TBD # FRACTIONAL PART (condition: qY and qX must be greater than zero???) if qDiff == 0: trunkExt = '' MSBqY = qY - 1 LSBqY = 0 MSBqX = qX - 1 LSBqX = 0 # Destination fractional part is == than the input one elif qDiff > 0: # Destination fractional part is > than the input one # Fill with zeros the unused bits trunkExt = '%(nameY)s({!s} downto 0) <= (others => \'0\');\n'.format(qDiff-1) MSBqY = qY - 1 LSBqY = qDiff MSBqX = qX - 1 LSBqX = 0 else: #qDiff < 0 # Destination fractional part is < than the input one trunkExt = '' MSBqY = qY - 1 LSBqY = 0 MSBqX = qX - 1 LSBqX = -1 * qDiff convertFPDict = { 'nameX': nameX, 'nameY': nameY, 'signExt': signExt, 'MSBpY': MSBpY, 'LSBpY': LSBpY, 'MSBpX': MSBpX, 'LSBpX': LSBpX, 'trunkExt': trunkExt, 'MSBqY': MSBqY, 'LSBqY': LSBqY, 'MSBqX': MSBqX, 'LSBqX': LSBqX } convertFPString = ( '%(signExt)s%(nameY)s(%(MSBpY)u downto %(LSBpY)u)' ' <= %(nameX)s(%(MSBpX)u downto %(LSBpX)u);\n' '%(trunkExt)s%(nameY)s(%(MSBqY)u downto %(LSBqY)u)' ' <= %(nameX)s(%(MSBqX)u downto %(LSBqX)u);\n' ) return convertFPString % convertFPDict def sequentialBlock(self, M, N, clkEdge='posedge', rstActive='high'): # TBD: different rst and clk polarity # Now, they are true and posedge respectively # Check the arguments if clkEdge == 'posedge': boolEdge = '1' elif clkEdge == 'negedge': boolEdge = '0' else: sys.exit('ERROR: active clock edge not recognized') if rstActive == 'high': boolRst = '1' elif rstActive == 'low': boolRst = '0' else: sys.exit('ERROR: reset active level not recognized') seqDict = { 'boolEdge': boolEdge, 'boolRst': boolRst } if M >= 1 or N >= 1: seqString = ( '-- Sequential block\n' 'z_block: process (clk)\n' ' begin\n' ' if (clk\'event and clk = \'%(boolEdge)s\') then\n' ' if (rst = \'%(boolRst)s\') then\n' ) for ii in range(M-1): seqString += ' zb%(ii)u <= (others => \'0\');\n' % {'ii': ii} for ii in range(N-1): seqString += ' za%(ii)u <= (others => \'0\');\n' % {'ii': ii} seqString += ' else\n' for ii in range(M-1): seqString += ' zb%(ii)u <= zb%(ii)u_next;\n' % {'ii': ii} for ii in range(N-1): seqString += ' za%(ii)u <= za%(ii)u_next;\n' % {'ii': ii} seqString += ( ' end if;\n' ' end if;\n' 'end process;\n' ) return seqString % seqDict def signalZ(self, M, N, busX, busY, busC, structure): signalZString = '-- Z^-1 delay blocks\n' if structure == 'Direct_Form_I_Transposed': # Z blocks in the b coefficients side for ii in range(M-1): signalZString += 'signal zb%(ii)u, zb%(ii)u_next: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} signalZString += '\n' # Z blocks in the a coefficients side for ii in range(N-1): if ii != 0: bus = busX + busC -1 -1 else: bus = busX -1 signalZString += 'signal za%(ii)u, za%(ii)u_next: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': bus} signalZString += '\n' return signalZString def signalC(self, M, N, busC, structure): signalCString = '-- Filter constants\n' if structure == 'Direct_Form_I_Transposed': # Z blocks in the b coefficients side for ii in range(M): signalCString += 'signal b%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busC - 1} signalCString += '\n' # Z blocks in the a coefficients side for ii in range(N): if ii != 0: signalCString += 'signal a%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busC - 1} signalCString += '\n' return signalCString def signalProducts(self, M, N, busX, busY, busC, structure): signalProductString = '-- Filter Products\n' if structure == 'Direct_Form_I_Transposed': # Products from B coeff for ii in range(M): signalProductString += 'signal pb%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} signalProductString += '\n' # Products from A coeff for ii in range(N): if ii != 0: signalProductString += 'signal pa%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} signalProductString += '\n' # Temporal Products from B coeff for ii in range(M): signalProductString += 'signal pb%(ii)u_temp: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1} signalProductString += '\n' # Temporal Products from A coeff for ii in range(N): if ii != 0: signalProductString += 'signal pa%(ii)u_temp: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1} signalProductString += '\n' return signalProductString def signalAdders(self, M, N, busX, busY, busC, structure): signalAddString = '-- Filter Adders\n' if structure == 'Direct_Form_I_Transposed': # Adders in the b coefficients side for ii in range(M-1): signalAddString += 'signal sb%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} signalAddString += '\n' # Adders in the a coefficients side for ii in range(N-1): if ii != 0: bus = busX + busC -1 -1 else: bus = busX -1 signalAddString += 'signal sa%(ii)u: signed(%(bus)u downto 0);\n' % {'ii': ii, 'bus': bus} signalAddString += '\n' return signalAddString def signalFeedback(self, M, N, busX, busY, busC, structure): signalFeedbackString = '' if structure == 'Direct_Form_I_Transposed': signalFeedbackString += '-- Feedback loop accumulator\n' signalFeedbackString += 'signal v: signed(%(bus)u downto 0);\n' % {'bus': busX -1} return signalFeedbackString def assignCoeff(self, M, N, bCoeffBin, bCoeffFloat, aCoeffBin, aCoeffFloat, structure): assignCoeffString = '-- Assign Coefficient\n' if structure == 'Direct_Form_I_Transposed': # Z blocks in the b coefficients side for ii in range(M): assignCoeffString += 'b%(ii)u <= \"%(BCoeffBin)s\"; -- %(BCoeffFloat)f\n' % {'ii': ii, 'BCoeffBin': bCoeffBin[ii], 'BCoeffFloat': bCoeffFloat[ii]} assignCoeffString += '\n' # Z blocks in the a coefficients side for ii in range(N): if ii != 0: assignCoeffString += 'a%(ii)u <= \"%(ACoeffBin)s\"; -- %(ACoeffFloat)f\n' % {'ii': ii, 'ACoeffBin': aCoeffBin[ii], 'ACoeffFloat': aCoeffFloat[ii]} assignCoeffString += '\n' return assignCoeffString def arithProduct(self, M, N, busX, busY, busC, structure): arithProductString = '-- Product Arithmetics\n' if structure == 'Direct_Form_I_Transposed': # Execute products from B side for ii in range(M): arithProductString += 'pb%(ii)u_temp <= v * b%(ii)u;\n' % {'ii': ii} arithProductString += 'pb%(ii)u <= pb%(ii)u_temp(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} arithProductString += '\n' # Execute products from A side for ii in range(N): if ii != 0: arithProductString += 'pa%(ii)u_temp <= v * a%(ii)u;\n' % {'ii': ii} arithProductString += 'pa%(ii)u <= pa%(ii)u_temp(%(bus)u downto 0);\n' % {'ii': ii, 'bus': busX + busC -1 -1} arithProductString += '\n' return arithProductString def arithAdders(self, M, N, structure): arithAddersString = '-- Adder Arithmetics\n' if structure == 'Direct_Form_I_Transposed': # Z blocks in the b coefficients side for ii in range(M-1): arithAddersString += 'sb%(ii)u <= pb%(ii)u + zb%(ii)u;\n' % {'ii': ii} arithAddersString += '\n' # Z blocks in the a coefficients side for ii in range(N-1): if ii == 0: arithAddersString += 'sa%(ii)u <= sig_in + za%(ii)u;\n' % {'ii': ii} else: arithAddersString += 'sa%(ii)u <= pa%(ii)u + za%(ii)u;\n' % {'ii': ii} arithAddersString += '\n' return arithAddersString def connectBlocks(self, M, N, busX, busY, busC, structure): connectBlocksString = '-- Structure Blocks Connection\n' if structure == 'Direct_Form_I_Transposed': # Z blocks in the b coefficients side for ii in range(M): if ii == 0: if M == 1: outputStage = 'pb0' else: outputStage = 'sb0' connectBlocksString += self.convertFixedPoint(outputStage, busC[0] + busX[0] -1, busC[1] + busX[1], 'sig_out', busY[0], busY[1]) else: if ii == (M-1): sourceTypeB = 'p' else: sourceTypeB = 's' bConnectDict = { 'SrcType': sourceTypeB, 'Src': ii, 'Dst': ii - 1 } connectBlocksString += 'zb%(Dst)u_next <= %(SrcType)sb%(Src)u;\n' % bConnectDict connectBlocksString += '\n' for ii in range(N): if ii == 0: if N == 1: connectBlocksString += 'v <= sig_in;\n' else: connectBlocksString += 'v <= sa0;\n' elif ii == 1: if ii == (N-1): firstStageA = 'pa1' else: firstStageA = 'sa1' connectBlocksString += self.convertFixedPoint(firstStageA, busC[0] + busX[0] -1, busC[1] + busX[1], 'za0_next', busX[0], busX[1]) else: if ii == (N-1): sourceTypeA = 'p' else: sourceTypeA = 's' aConnectDict = { 'SrcType': sourceTypeA, 'Src': ii, 'Dst': ii - 1 } connectBlocksString += 'za%(Dst)u_next <= %(SrcType)sa%(Src)u;\n' % aConnectDict connectBlocksString += '\n' return connectBlocksString def verilogTestBench(self, name, stimulusBin, stimulusFloat, busX, busY): # Write Verilog Dictionary testBenchDict = { 'Name': name, 'BusX': busX, 'BusY': busY, 'MSBX': busX - 1, 'MSBY': busY - 1, 'ClkPosWidth': 25, 'ClkNegWidth': 25, } testBenchString = ( 'module tb_%(Name)s;\n' '\n' 'reg clk;\n' 'reg rst;\n' 'reg [%(MSBX)u:0] sig_in;\n' 'wire [%(MSBY)u:0] sig_out;\n' '\n' '%(Name)s dut(\n' ' clk,\n' ' rst,\n' ' sig_in,\n' ' sig_out\n' ');\n' '\n' 'always begin\n' ' clk <= 1;\n' ' #%(ClkPosWidth)u;\n' ' clk <= 0;\n' ' #%(ClkNegWidth)u;\n' 'end\n' '\n' '\n' 'initial begin\n' ' $dumpfile(\"%(Name)s.vcd\");\n' ' $dumpvars;\n' 'end\n' '\n' 'initial begin\n' ' sig_in <= %(BusX)u\'b0; // signal is initially zero\n' ' rst <= 1\'b1; #100;\n' ' rst <= 1\'b0; #150;\n' ) % testBenchDict for ii in range(len(stimulusBin)): stimulusDict = { 'BusX': busX, 'CoeffBin': stimulusBin[ii], 'CoeffFloat': stimulusFloat[ii] } tempString = ' #50; sig_in <= %(BusX)u\'b%(CoeffBin)s; // %(CoeffFloat)f\n' testBenchString += tempString % stimulusDict testBenchString += ( ' $finish;\n' 'end\n' '\n' '\n' 'endmodule\n' ) return testBenchString def vhdlTestBench(self, name, stimulusBin, stimulusFloat, busX, busY): # Write Verilog Dictionary testBenchDict = { 'Name': name, 'BusX': busX, 'BusY': busY, 'MSBX': busX - 1, 'MSBY': busY - 1, 'ClkPosWidth': 25, 'ClkNegWidth': 25, } testBenchString = ( 'LIBRARY ieee;\n' 'USE ieee.std_logic_1164.ALL;\n' 'USE ieee.numeric_std.all;\n' '\n' 'ENTITY tb_%(Name)s IS\n' 'END tb_%(Name)s;\n' '\n' 'ARCHITECTURE behavior OF tb_%(Name)s IS\n' ' -- Component Declaration for the Unit Under Test (UUT)\n' ' COMPONENT %(Name)s\n' ' PORT(\n' ' clk: IN std_logic;\n' ' rst: IN std_logic;\n' ' sig_in: IN signed(%(MSBX)u downto 0);\n' ' sig_out: OUT signed(%(MSBY)u downto 0)\n' ' );\n' ' END COMPONENT;\n' '\n' ' -- Signal Declaration\n' ' signal clk : std_logic := \'0\';\n' ' signal rst : std_logic := \'1\';\n' ' signal sig_in : signed(%(MSBX)u downto 0);\n' ' signal sig_out : signed(%(MSBY)u downto 0);\n' '\n' 'BEGIN\n' ' -- Instantiate the Unit Under Test (UUT)\n' ' uut: %(Name)s PORT MAP (\n' ' clk => clk,\n' ' rst => rst,\n' ' sig_in => sig_in,\n' ' sig_out => sig_out\n' ' );\n' '\n' ' clk_process : process\n' ' begin\n' ' clk <= \'1\';\n' ' wait for %(ClkPosWidth)u ns;\n' ' clk <= \'0\';\n' ' wait for %(ClkNegWidth)u ns;\n' ' end process;' '\n' ' stimulus_process : process\n' ' begin\n' ' sig_in <= (others => \'0\');\n' ' rst <= \'1\'; wait for 100 ns;\n' ' rst <= \'0\'; wait for 125 ns;\n' ) % testBenchDict for ii in range(len(stimulusBin)): stimulusDict = { 'BusX': busX, 'CoeffBin': stimulusBin[ii], 'CoeffFloat': stimulusFloat[ii] } tempString = ' wait for 50 ns; sig_in <= "%(CoeffBin)s"; -- %(CoeffFloat)f\n' testBenchString += tempString % stimulusDict testBenchString += ( #' wait;\n' #' -- Terminate Simulation\n' #' assert false report "Simulation completed" severity failure;\n' ' end process;\n' '\n' 'END;\n' ) return testBenchString