見出し画像

13.レジスタを実装して和差算回路を作る

ここではCPU側に対しての一般的なインターフェースとレジスタを実装について触れていきます。

問題

以下の回路モジュール(A+B、A-B)を作成してください。
<レジスタ解説書>
アドレス 属性      解説      データ  
0x00   リードライト  Aの数      8bit
0x01   リードライト  Bの数    8bit
0x02           リード                  和の結果     8bit
0x03   リード     差の結果  8bit 
#0x は16進数表記
#計算結果は負の数も扱う(ビット7は正負符号、ビット6:0はデータ)

入力信号解説
adr[1:0]   2ビットのアドレス信号
wdata[7:0] 8ビットの演算入力信号(A、B)
cs_n    チップ(モジュール)セレクト信号 Lアクティブ
wr_n    ライト信号 Lアクティブ
rd_n    リード信号 Lアクティブ
reset_n   リセット信号 非同期リセットLアクティブ

出力信号
rdata[7:0]  8ビットの演算結果出力信号

シンボル図
動作イメージ図

さてどう作る?

いままでと違い問題が一見複雑になったようにみえますが恐れることはありません。変わったところは入力情報がCPUインターフェースライクになったことです。このインターフェースはおおよそ以下のようになります。
ライト・・cs_nかつwr_nがLのとき指定されたadrのデータwdataをライト
リード・・cs_nかつrd_nがLのとき指定されたadrにデータrdataをリード
ということで、
 ・CPUインターフェースに従ってレジスタ解説書にある4つのレジスタを作る
 ・足し算回路を作る
 ・引き算回路を作る
と大別すると3つの回路を作れば良さそうです。

VHDLで書いてみる

以下の様になります。それほど難しくありません。
上から1番目と2番目の process(clk, reset_n) では、アドレス”00”と”01”にレジスタライトアクセスに対応したコードとなります。
wire10 <= reg00 + reg01;
wire11 <= reg00 - reg01; で上記レジスタに書かれた値を即時計算(和と差)しています。
with adr select と3番目のprocess(clk, reset_n)ではレジスタリードアクセスに対応したコードとなります。
最後に大事なことを言い忘れました。冒頭の
use IEEE.STD_LOGIC_SIGNED.ALL;
は負の数も扱うという宣言になります。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;

entity id is
    Port (
        clk     : in  STD_LOGIC;
        reset_n : in  STD_LOGIC;
        adr     : in  STD_LOGIC_VECTOR (1 downto 0);
        cs_n    : in  STD_LOGIC;
        wr_n    : in  STD_LOGIC;
        wdata   : in  STD_LOGIC_VECTOR (7 downto 0);
        rd_n    : in  STD_LOGIC;
        rdata   : out STD_LOGIC_VECTOR (7 downto 0)
    );
end id;

architecture rtl of id is
    signal reg00, reg01 : STD_LOGIC_VECTOR (7 downto 0);
    signal wire10, wire11 : STD_LOGIC_VECTOR (7 downto 0);
    signal rdata_int : STD_LOGIC_VECTOR (7 downto 0);
begin

    process(clk, reset_n)
    begin
        if reset_n = '0' then
            reg00 <= (others => '0');
        elsif rising_edge(clk) then
            if cs_n = '0' and wr_n = '0' and adr = "00" then
                reg00 <= wdata;
            end if;
        end if;
    end process;

    process(clk, reset_n)
    begin
        if reset_n = '0' then
            reg01 <= (others => '0');
        elsif rising_edge(clk) then
            if cs_n = '0' and wr_n = '0' and adr = "01" then
                reg01 <= wdata;
            end if;
        end if;
    end process;

    wire10 <= reg00 + reg01;
    wire11 <= reg00 - reg01;

    -- 優先順位がないマルチプレクサ
    with adr select
        rdata_int <= reg00 when "00",
                     reg01 when "01",
                     wire10 when "10",
                     wire11 when "11",
                     (others => '0') when others;

    process(clk, reset_n)
    begin
        if reset_n = '0' then
            rdata <= (others => '0');
        elsif rising_edge(clk) then
            if cs_n = '0' and rd_n = '0' then
                rdata <= rdata_int;
            end if;
        end if;
    end process;

end rtl;

Verilogで書いてみる

VHDLコードをChatGPTでVerilog変換してみました。負の数を扱うということで
reg signed [7:0] reg00, reg01;
wire signed [7:0] wire10, wire11;
としているところがポイントでしょうか。

module id (
    input wire clk,
    input wire reset_n,
    input wire [1:0] adr,
    input wire cs_n,
    input wire wr_n,
    input wire [7:0] wdata,
    input wire rd_n,
    output reg [7:0] rdata
);

    reg signed [7:0] reg00, reg01;
    wire signed [7:0] wire10, wire11;
    reg [7:0] rdata_int;

    assign wire10 = reg00 + reg01;
    assign wire11 = reg00 - reg01;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            reg00 <= 8'b0;
        end else if (!cs_n && !wr_n && adr == 2'b00) begin
            reg00 <= wdata;
        end
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            reg01 <= 8'b0;
        end else if (!cs_n && !wr_n && adr == 2'b01) begin
            reg01 <= wdata;
        end
    end

    always @(*) begin
        case (adr)
            2'b00: rdata_int = reg00;
            2'b01: rdata_int = reg01;
            2'b10: rdata_int = wire10;
            2'b11: rdata_int = wire11;
            default: rdata_int = 8'b0;
        endcase
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            rdata <= 8'b0;
        end else if (!cs_n && !rd_n) begin
            rdata <= rdata_int;
        end
    end

endmodule

シミュレーション結果

実は動作イメージ図がシミュレーション結果になっていますが、再度掲載します。
左からAの値3を書き込み、Bの値1を書き込み、和の結果と差の結果をリードし、Aの値とBの値をリードし、Bの値4を書き込み、最後に差の結果をリードしています。最後のrdataが0xFFとなっていますが、これは-1を意味しています。

シミュレーション図

おわりに

今回はCPU側に対しての一般的なインターフェースとレジスタを実装し、和差算についてご紹介しました。一通り見てみるとそれほど難しくないことが体感できたかと思います。ありがとうございました。


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