見出し画像

14.レジスタを実装して汎用I/Oポートを作る

ここではCPU側に対しての一般的なインターフェースとレジスタを実装するとともに汎用I/Oポートの機能を持つ回路作成について触れていきます。

問題

以下の回路モジュール(8本汎用I/Oポート)を作成してください。
<レジスタ解説書>
アドレス 属性      解説     データ  
0x00   リードライト  ポート出力値  8bit
0x01   リードライト  ポート方向   8bit
             (H:出力 L:入力)
0x02      リードライト    ポート入力値  8bit 
#0x は16進数表記

シンボル図

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

出力信号
rdata[7:0]  8ビットの演算結果出力信号(CPU側 I/F用)
pt[7:0]             汎用ポート(8本)

動作イメージ図

さてどう作る?

汎用ポートを考える前に、まず双方向I/Oのポートとはどういうものかについて頭にイメージしておく必要があると思います。今回は以下をイメージしています。

双方向I/O

ibinは外部pt端子から入力される信号
iboutは外部pt端子から出力される信号
ibcntはこのI/Oを入力とするか出力とするかを決める信号
(H:出力 L:入力)
ibcntがLならばZ(ハイインピーダンス)となるので入力ポートとして指示したこととなります。お分かりになりますでしょうか。

あとは前章(13.レジスタを実装して和差算回路を作る)同様にレジスタ解説書を見て実装していけばよいです。

VHDLで書いてみる

主に5つのprocess文で構成しています。
上から順にこんな感じになっています。
 ・ポート出力値を設定するレジスタ
 ・ポート入力値を格納する
 ・ポートの入出力を設定するレジスタ
 ・各レジスタの値をリードする
 ・ポート8本を作る

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

entity pt8 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);
           	pt		: inout STD_LOGIC_VECTOR (7 downto 0));
end pt8;

architecture rtl of pt8 is
	signal ibout,iregin,ibin,ibcnt	:STD_LOGIC_VECTOR (7 downto 0);

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


   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
                case adr is
                    when "00" =>
                        rdata <= ibout;
                    when "01" =>
                        rdata <= ibcnt;
                    when "10" =>
                        rdata <= iregin;
                    when others =>
                        rdata <= (others => '0');
                end case;
            end if;
        end if;
    end process;
 
 
	ibin <= pt;

	process(ibcnt,ibout)
	begin
		for i in 0 to 7 loop
			if ibcnt(i) = '0' then
   			pt(i) <= 'Z';
   		else pt(i) <= ibout(i);
   		end if;
		end loop;
	end process;
	
end rtl;

Verilogで書いてみる

VHDLコードをChatGPTでVerilog変換してみました。うまく変換できているようには見えます。

module pt8 (
    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,
    inout wire [7:0] pt
);

    reg [7:0] ibout, iregin, ibin, ibcnt;

    // Process for writing to ibout
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            ibout <= 8'b0;
        end else if (!cs_n && !wr_n && adr == 2'b00) begin
            ibout <= wdata;
        end
    end

    // Process for writing to iregin
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            iregin <= 8'b0;
        end else begin
            iregin <= ibin;
        end
    end

    // Process for writing to ibcnt
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            ibcnt <= 8'b0;
        end else if (!cs_n && !wr_n && adr == 2'b01) begin
            ibcnt <= wdata;
        end
    end

    // Process for reading data
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            rdata <= 8'b0;
        end else if (!cs_n && !rd_n) begin
            case (adr)
                2'b00: rdata <= ibout;
                2'b01: rdata <= ibcnt;
                2'b10: rdata <= iregin;
                default: rdata <= 8'b0;
            endcase
        end
    end

    // Assign pt to ibin
    assign ibin = pt;

    // Process for controlling pt based on ibcnt and ibout
    always @(*) begin
        integer i;
        for (i = 0; i < 8; i = i + 1) begin
            if (ibcnt[i] == 1'b0) begin
                pt[i] = 1'bz;
            end else begin
                pt[i] = ibout[i];
            end
        end
    end

endmodule

シミュレーション結果

図に直接解説しましたのでご覧ください。

シミュレーション結果

おわりに

今回はCPU側に対しての一般的なインターフェースとレジスタを実装し、汎用I/Oポートの作成についてご紹介しました。双方向I/Oポートとはどういうものかを理解する必要がありますが、逆に言うとそれが分かっていれば難しくありません。ありがとうございました。

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