見出し画像

ALTERA Quartus2 v13.1 で内蔵メモリが使えなかったので強引にフリップフロップ群で構成した

テストパターンはグラフィカルに描きたいので・・

「なんで、いまどきQuartus prime使ってないの?」というあなた、「正論です、その通りです」なんですが、シミュレーション時にテストベンチをコードで書くのが嫌だったので、テストパターンをグラフィカルに書けるUniversity Program VWFが使えるバージョンのQuartus2を使って遊んでみました。

内蔵メモリを作る

Tools ->Megawizard plug-in managerで簡単にメモリを作れます。
まあこんな感じで・・ ポチポチ押すだけの仕事です。

M10Kの内蔵メモリ(256ワード*8bit)を作成してる図

ところがシミュレーション時にM10Kなど作ったメモリが使えない

テストパターンを書いた図(University Program VWF)

テストパターンを File -> New -> Verification/Debugging Files -> University Program WF から上図の通りグラフィカルに書きまして(1分もかからない)、下図の赤いところをポチッと押してシミュレーション開始!

論理シミュレーションを走らせる図

大変です 大変です 赤文字でエラーが出てます

ところがですね、数秒後にエラーがでるのでした。ライブラリがどうちゃらこうちゃら言われてますけど、VHDLで内蔵メモリ作ったからダメだったかな?? Verilogなら良さげな感じが見受けられます。が。

シミュレーション時のエラーメッセージ図

仕方がないので、内蔵メモリをフリップフロップ群で構成させてみる

Megawizard で吐きだした内蔵メモリ使えないじゃん、ってことでVHDLコードでメモリ相当のコードを書く。(10分程度)、ちなみにLCsと同じ動きをするメモリじゃなかったフリップフロップ群(256ワード*8bit)です。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ff1port is
	port (	address	: in std_logic_vector(7 downto 0);
			data 	: in std_logic_vector(7 downto 0);
			we		: in std_logic;
			inclock	: in std_logic;
			outclock: in std_logic;
			q		: out std_logic_vector(7 downto 0));
end ff1port;

architecture rtl of ff1port is
	-- Define an array of 256 registers, each 8 bits wide
	type reg_array is array(255 downto 0) of std_logic_vector(7 downto 0);
	signal registers : reg_array := (others => (others => '0'));  -- Initialize all to 0

begin
	-- Writing process
	process(inclock)
	begin
		if rising_edge(inclock) then
			if we = '1' then
				registers(conv_integer(address)) <= data;
			end if;
		end if;
	end process;

	-- Reading process
	process(outclock)
	begin
		if rising_edge(outclock) then
			q <= registers(conv_integer(address));
		end if;
	end process;
	
end rtl;

これをそのまま論理合成するとダメなので・・

「じゃあ、このコードを論理合成してシミュレーションしてOKね」と言いたいところなんですが、この論理合成ツールが優秀なので「こいつはフリップフロップの面をかぶったメモリである、内蔵メモリとして割り当てしよっ」ってなってしまって、さっきのエラーがまた出るのでありました。

強制的に内蔵メモリとして割り当てない方法をChatGPTが教えてくれた

それでChatGPTに「強制的に内蔵メモリとして割り当てない方法」を聞いてみたところ、すぐに答えが返ってきました。「プロジェクトファイル内にある.qsfファイルに以下の1行を足し給え。」 なんだそれだけっすか。

set_global_assignment -name AUTO_RAM_RECOGNITION OFF

おお、レジスタ(フリップフロップ)として割り当てられている

以下の論理合成中の図を見てください。(赤枠を押すとスタートする)
青枠のところがレジスタとしてアサインされたことを示していますね。
(256ワード*8+8=2056となってます)

論理合成中の図

メモリコントローラ(というかインターフェース変換)作りました

このメモリ代替フリップフロップ群はLCsの動作、インターフェースに合わせて作りましたが恐らく省エネ動作を狙った独特なインターフェースとなっているので一般的なチップセレクト、ライト、リードなどのインタフェースに合うように変換するモジュールを用意しました。と言ってもたいしたコードではないので説明するまでもないでしょう。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity mem_con2 is
    Port ( 	clk : in  STD_LOGIC;
           	reset_n : in  STD_LOGIC;
           	cs_n 	:in std_logic;
           	wr_n	:in STD_LOGIC;
           	rd_n	:in STD_LOGIC;
           	adri	:in std_logic_vector(7 downto 0);
           	wdatai	:in std_logic_vector(7 downto 0);
           	rdatai	:in std_logic_vector(7 downto 0);
           	we 		:out  STD_LOGIC;
           	adro	:out std_logic_vector(7 downto 0);
           	wdo		:out std_logic_vector(7 downto 0);
           	inclocko:out  STD_LOGIC;
           	outclocko:out  STD_LOGIC;
           	rdatao	:out std_logic_vector(7 downto 0);
           	wait_n : out std_logic);
end mem_con2;

architecture rtl of mem_con2 is
    signal istatecntw :std_logic_vector(1 downto 0);
    signal istatecntr :std_logic_vector(2 downto 0);
    signal	iwait_n1,iwait_n2	:std_logic;
    

begin
    process(clk, reset_n)
    begin
        if reset_n = '0' then
            istatecntw <= (others=>'0');
        elsif rising_edge(clk) then
        	if istatecntw = "10" then
        		istatecntw <= (others=>'0');
        	elsif cs_n = '0' and wr_n ='0'then
				istatecntw <= istatecntw + '1';
			elsif istatecntw /= "00" then
				istatecntw <= istatecntw + '1';
        	end if;
        end if;
    end process;
    
    
    process(istatecntw )
    begin
        if istatecntw = "10" then
			inclocko <= '1';
		else inclocko <= '0';
        end if;
    end process;
    
    process(istatecntw )
    begin
        if istatecntw /= "00" then
			we <= '1';
		else we<= '0';
        end if;
    end process;
    
    process(istatecntw )
    begin
        if istatecntw = "01" then
			iwait_n1 <= '0';
		else iwait_n1<= '1';
        end if;
    end process;
    
    wait_n <= iwait_n1 and iwait_n2;
    
    process(clk, reset_n)
    begin
        if reset_n = '0' then
            adro <= (others=>'0');
        elsif rising_edge(clk) then
        	adro <= adri;
        end if;
    end process;
    
    process(clk, reset_n)
    begin
        if reset_n = '0' then
            wdo <= (others=>'0');
        elsif rising_edge(clk) then
        	wdo <= wdatai;
        end if;
    end process;

    process(clk, reset_n)
    begin
        if reset_n = '0' then
            istatecntr <= (others=>'0');
        elsif rising_edge(clk) then
        	if istatecntr = "100" then
        		istatecntr <= (others=>'0');
        	elsif cs_n = '0' and rd_n ='0'then
				istatecntr <= istatecntr + '1';
			elsif istatecntr /= "000" then
				istatecntr <= istatecntr + '1';
        	end if;
        end if;
    end process;

    process(istatecntr )
    begin
        if istatecntr = "011" then
			outclocko <= '1';
		else outclocko<= '0';
        end if;
    end process;
    
    process(istatecntr )
    begin
        if istatecntr = "000" then
			iwait_n2 <= '1';
		else iwait_n2<= '0';
        end if;
    end process;
    
    process(clk, reset_n)
    begin
        if reset_n = '0' then
            rdatao <= (others=>'0');
        elsif rising_edge(clk) then
        	rdatao <= rdatai;
        end if;
    end process;
    
end rtl;

トップ階層でコントローラとフリップフロップ群をつなぐ

ただつなぐだけのコードです。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity mem_test2 is
    Port ( 	clk 	: in STD_LOGIC;
           	reset_n : in STD_LOGIC;
           	adr		: in std_logic_vector(7 downto 0);
           	wdata	: in STD_LOGIC_VECTOR (7 downto 0);
           	cs_n	: in STD_LOGIC;
           	wr_n	: in STD_LOGIC;
           	rd_n	: in STD_LOGIC;
           	wait_n  : out std_logic;           	
           	rdata	: out STD_LOGIC_VECTOR (7 downto 0);
				db_iaddress : out std_logic_vector(7 downto 0);
				db_idata	: out std_logic_vector(7 downto 0);
				db_iwe	: out std_logic; 
				db_iinclocko : out std_logic;
				db_ioutclocko :out std_logic;
				db_iq :out std_logic_vector(7 downto 0));
end mem_test2;

architecture rtl of mem_test2 is
	signal iq,iaddress,idata		:std_logic_vector (7 downto 0);
	signal iwe,iinclocko ,ioutclocko		:std_logic;
	
	component mem_con2
    Port ( 	clk : in  STD_LOGIC;
           	reset_n : in  STD_LOGIC;
           	cs_n 	:in std_logic;
           	wr_n	:in STD_LOGIC;
           	rd_n	:in STD_LOGIC;
           	adri	:in std_logic_vector(7 downto 0);
           	wdatai	:in std_logic_vector(7 downto 0);
           	rdatai	:in std_logic_vector(7 downto 0);
           	we 		:out  STD_LOGIC;
           	adro	:out std_logic_vector(7 downto 0);
           	wdo		:out std_logic_vector(7 downto 0);
           	inclocko:out  STD_LOGIC;
           	outclocko:out  STD_LOGIC;
           	rdatao	:out std_logic_vector(7 downto 0);
           	wait_n : out std_logic);
    end component;
    
    component ff1port
	Port
	(
		address		: IN STD_LOGIC_VECTOR (7 DOWNTO 0) ;
		data		: IN STD_LOGIC_VECTOR (7 DOWNTO 0); 
		inclock		: IN STD_LOGIC ;
		outclock	: IN STD_LOGIC ;
		we			: IN STD_LOGIC ;
		q			: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
	);
	end component;

begin

	u0:mem_con2
		port map( 	clk 	=> clk,
           			reset_n => reset_n,
           			cs_n 	=> cs_n,
           			wr_n	=> wr_n,
           			rd_n	=> rd_n,
           			adri	=> adr,
           			wdatai	=> wdata,
           			rdatai	=> iq,
           			we 		=> iwe,
           			adro	=> iaddress,
           			wdo		=> idata,
           			inclocko=> iinclocko,
           			outclocko=>ioutclocko,
           			rdatao	=> rdata,
           			wait_n => wait_n);
    
    u1:ff1port
    	port map(	address	=> iaddress,
					data	=> idata,
					inclock	=> iinclocko,
					outclock=>ioutclocko,
					we		=> iwe,
					q		=> iq);

	db_iaddress <= iaddress;
	db_idata	<= idata;
	db_iwe	<= iwe; 
	db_iinclocko <= iinclocko;
	db_ioutclocko <= ioutclocko;
	db_iq <= iq;
					
end rtl;

トップ階層のプロジェクトファイル内の.qsfファイルに以下を記載する(内蔵メモリをアサインしないようにする)ことを忘れずに・・
これ重要!

set_global_assignment -name AUTO_RAM_RECOGNITION OFF

動いた!

青はライト、赤はリードですね。(データは誕生日っぽいですが私の誕生日ではありません)

シミュレーション結果の図

どれだけの需要があるか分かりませんが、記録として残しておきたいと思います。ありがとうございました。


この記事が気に入ったらサポートをしてみませんか?