見出し画像

chatGPTで簡単な回路をVHDLで生成する~その1

はじめに

はじめましてmiczと申します。インクジェットプリンターやレーザープリンタ、複写機のASICをごろごろ作っていた者です。巷で「chatGPTは僕ちゃんより優秀なコード吐くよ」との話が絶えないので、「所詮言語モデルなんだから大したことはできないんでしょ」「さすがにマイナーなハードウエア記述言語は無理なんでしょ」と思いつつも、「やさしくインプットしてあげれば簡単なのはいけるんじゃないの」との期待を持ちつつ遊んでみました。

2段シフトレジスタを作る

手始めに2段のシフトレジスタを作ってみます。
入力は雑にこんな感じで。
VHDL言語を使用して2段シフトレジスタの回路作成してください。入力はD、クロックはCLK、リセットはRESETNでローアクティブリセットかつ非同期リセット、出力はQとします。Qは2段目のレジスタの出力とします。

これぐらいならインターネットにごろごろ転がっているので汎用言語モデルでも出せるでしょ、見せてくれたまへchatGPT君。

ということでこうなりました。

library ieee;
use ieee.std_logic_1164.all;

entity shift_register is
    port (
        D: in std_logic;              -- 入力信号
        CLK: in std_logic;            -- クロック
        RESETN: in std_logic;         -- リセット信号
        Q: out std_logic              -- 出力信号
    );
end entity;

architecture rtl of shift_register is
    signal Q1, Q2: std_logic;         -- 内部信号
begin
    process (CLK, RESETN)
    begin
        if RESETN = '0' then          -- リセットがアクティブな時
            Q1 <= '0';                -- Q1とQ2をリセットする
            Q2 <= '0';
        elsif rising_edge(CLK) then   -- クロックの立ち上がりエッジで処理する
            Q1 <= D;                  -- 入力信号を1段目のレジスタに格納
            Q2 <= Q1;                 -- 1段目のレジスタの出力を2段目のレジスタに格納
        end if;
    end process;

    Q <= Q2;                          -- 2段目のレジスタの出力をQに接続する
end architecture;

で、説明が以下の通り

この回路は、クロックの立ち上がりエッジで入力信号Dを1段目のレジスタに格納し、1段目のレジスタの出力を2段目のレジスタに格納します。2段目のレジスタの出力が出力信号Qに接続されます。リセット信号RESETNがアクティブになった場合、Q1とQ2がリセットされ、出力信号Qは'0'になります。

完璧ですね! まあできるとは思ったけどきれいにコメントまで付けてるし。

4個の8ビットのレジスタ群を作る

じゃあ次に「4個のレジスタを作ってライト信号と同期して該当アドレス部に書き込む。そのブロックのデータ出力は外部のリードセレクタ信号に従って該当レジスタのデータを出す」ってのはどうでしょうかね。ってことで以下チャレンジ。

もはや日本語仕様書・・
VHDL言語を使用して以下の仕様に従い回路を作成してください。分からないことがあれば私に聞いてください。  
・ブロック名:REG  
・入力信号:    
  アドレスバス:信号名はADR:バス幅2ビット    
  データバス:信号名はDAT:バス幅8ビット    
  ライト:信号名はWR_N    
  クロック:信号名はCLK    
  リセット:信号名はRESET_N    
  リードセレクタ:信号名はRDSEL:バス幅2ビット  
・レジスタ仕様:    
  アドレス0x0:レジスタ名はレジスタ0    
  アドレス0x1:レジスタ名はレジスタ1    
  アドレス0x2:レジスタ名はレジスタ2    
  アドレス0x3:レジスタ名はレジスタ3  
・出力信号    
  データ:信号名はDATO:バス幅8ビット  
・出力信号DATOの制御    
  RDSELが"00"の時は、レジスタ0のデータを出力する    
  RDSELが"01"の時は、レジスタ1のデータを出力する    
  RDSELが"10"の時は、レジスタ2のデータを出力する    
  RDSELが"11"の時は、レジスタ3のデータを出力する    
・その他仕様    
  信号名の後ろに_Nがあればローアクティブ    
  非同期リセット    
  立ち上がり同期クロック    
  レジスタはWR_Nがアクティブな時にライトする    
  レジスタはライトオンリー

でこうなりました。

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity REG is
    port(
        ADR : in std_logic_vector(1 downto 0);
        DAT : in std_logic_vector(7 downto 0);
        WR_N : in std_logic;
        CLK : in std_logic;
        RESET_N : in std_logic;
        RDSEL : in std_logic_vector(1 downto 0);
        DATO : out std_logic_vector(7 downto 0)
    );
end entity;

architecture rtl of REG is

    type reg_array is array (0 to 3) of std_logic_vector(7 downto 0);
    signal reg : reg_array := (x"00", x"00", x"00", x"00");
    signal prev_clk : std_logic := '0';

begin

    process(CLK, RESET_N)
    begin
        if RESET_N = '0' then
            reg <= (x"00", x"00", x"00", x"00");
        elsif rising_edge(CLK) then
            if WR_N = '0' then
                case ADR is
                    when "00" =>
                        reg(0) <= DAT;
                    when "01" =>
                        reg(1) <= DAT;
                    when "10" =>
                        reg(2) <= DAT;
                    when "11" =>
                        reg(3) <= DAT;
                    when others =>
                        null; -- do nothing
                end case;
            end if;
        end if;
    end process;

    process(RDSEL, reg)
    begin
        case RDSEL is
            when "00" =>
                DATO <= reg(0);
            when "01" =>
                DATO <= reg(1);
            when "10" =>
                DATO <= reg(2);
            when "11" =>
                DATO <= reg(3);
            when others =>
                null; -- do nothing
        end case;
    end process;

end architecture;

で、説明が以下の通り

この回路は、REGというエンティティで定義され、以下のようなポートを持っています。

  • ADR : 2ビットのアドレスバス

  • DAT : 8ビットのデータバス

  • WR_N : ライト信号

  • CLK : クロック信号

  • RESET_N : リセット信号

  • RDSEL : 2ビットのリードセレクタ

  • DATO : 8ビットの出力データ

回路の内部では、reg_arrayという型で、4つの8ビットレジスタを宣言しています。レジスタの初期値はすべてx"00"で、非同期リセットによってすべてのレジスタがリセットされます。
回路は、立ち上がりエッジで動作するプロセスで制御されます。ライト信号WR_Nがアクティブな場合、アドレスADRに対応するレジスタにデータバスのデータを書き込みます。それ以外の場合、リードセレクタRDSELに対応するレジスタのデータを出力します。

できてますね。論理合成までかけてチェックしていませんが。
signal prev_clk : std_logic := '0'; は不要かと思うのですが、まあ素晴らしいです。HDLに慣れた人なら、「直接人間が書いた方が早いんだよ」と怒られそうですが、汎用言語モデルでどこまでできるのかを試してみたかっただけなので、、

楽しくなってきたのでもう少し使ってみようと思います。お付き合いくださりありがとうございました。

この記事が参加している募集

やってみた

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