pi_atb.v 8.78 KB
// pi_atb.v v1 Frank Berndt
// pi address translation block;
// :set tabstop=4

module pi_atb (
	sysclk, reset,
	atb_start, atb_stop, atb_vaddr, atb_udev, atb_pio, atb_map, atb_inv,
	atb_done, atb_paddr, atb_pdev, atb_piv, atb_err,
	atb_addr, atb_req, atb_ack, atb_h, atb_l
);
	// module io ports;

	input sysclk;				// system clock;
	input reset;				// system reset;

	input atb_start;			// start atb lookup;
	input atb_stop;				// stop atb operation;
	input [29:14] atb_vaddr;	// virtual block address;
	input [1:0] atb_udev;		// unmapped device;
	input atb_pio;				// pio access;
	input atb_map;				// do virtual to physical mapping;
	input atb_inv;				// invalidate atb cache;
	output atb_done;			// atb lookup done or error;
	output [29:14] atb_paddr;	// physical block address;
	output [1:0] atb_pdev;		// physical device;
	output atb_piv;				// use buffer iv;
	output atb_err;				// atb mapping/access error;

	output [7:0] atb_addr;		// atb buffer address;
	output atb_req;				// request atb buffer access;
	input atb_ack;				// atb read acknowledge;
	input [40:0] atb_h;			// upper atb entry from buffer;
	input [40:0] atb_l;			// lower atb entry from buffer;

	// address translation logic;
	// smallest virtual block is 16kbytes;
	// binary search through atb to find matching entry;
	// replace virtual block address with device physical address;
	// check access rights;

	// cache last lookup;
	// used to avoid translations when same block is hit;
	// invalidate cache on unmapped request;
	// invalidate cache when told by atb_inv;
	// invalidate cache on atb stop;

	reg atb_stdel;			// delayed atb_start;
	reg [29:14] atb_cache;	// last block looked up;
	reg atb_cval;			// atb cache is valid;
	reg atb_chit;			// atb cache hit;
	wire atb_go;			// start new atb lookup;
	wire atb_hit;			// hit previous lookup;
	wire atb_update;		// update physical result;

	always @(posedge sysclk)
	begin
		atb_stdel <= atb_start;
		if(atb_inv | atb_stop)
			atb_cval <= 0;
		else if(atb_update)
			atb_cval <= atb_map;
		atb_chit <= atb_cval & (atb_cache == atb_vaddr);
	end

	assign atb_go = atb_stdel & ~atb_chit;
	assign atb_hit = atb_stdel & atb_chit;

	// atb request control;

	wire atb_reset;			// reset atb logic;
	wire atb_srch;			// search atb for match;
	wire atb_pass;			// do not map, pass trough;
	reg [3:0] atb_pipe;		// atb control pipe;
	wire atb_next;			// advance atb pipe;
	wire atb_kick;			// kick off an atb cycle;
	wire atb_end;			// at end of atb buffer;
	reg atb_done;			// atb lookup done;
	wire atb_match;			// found match;
	wire atb_miss;			// miss in atb;

	assign atb_srch = atb_go & atb_map;
	assign atb_pass = atb_go & ~atb_map;

	assign atb_reset = reset | atb_stop;
	assign atb_next = atb_go | atb_ack | (|atb_pipe[3:1]);
	assign atb_kick = atb_srch | (atb_pipe[3] & ~atb_end);
	assign atb_end = atb_match | atb_miss;

	always @(posedge sysclk)
	begin
		if(atb_reset)
			atb_pipe <= 4'd0;
		else if(atb_next)
			atb_pipe <= { atb_pipe[2:0], atb_kick };
		atb_done <= atb_pass | atb_hit | (atb_end & atb_pipe[3]);
	end

	// atb address control;
	// binary search limited between 160..255;
	// start at buffer index 192;

	reg [6:0] atb_raddr;		// atb buffer request address;
	reg [6:0] atb_abit;			// binary search address bit;
	wire [6:0] atb_ashr;		// address bit shifted right;
	wire [6:0] atb_axor;		// address bit to fix;
	reg atb_up;					// search higher atb entries;

	assign atb_ashr = { 1'b0, atb_abit[6:1] };
	assign atb_axor = (atb_abit & {7{~atb_up}});

	always @(posedge sysclk)
	begin
		if(atb_reset | atb_go) begin
			atb_raddr <= 7'd64;
			atb_abit <= 7'b1000000;
		end else if(atb_pipe[3]) begin
			atb_raddr <= (atb_raddr ^ atb_axor) | atb_ashr;
			atb_abit <= atb_ashr;
		end
	end

	assign atb_req = atb_pipe[0];
	assign atb_addr = { 1'b1, atb_raddr };

	// atb miss detection;
	// when the address would cross the lower or upper bound;

	wire [1:0] atb_lim;			// atb address limits;

	assign atb_lim[0] = (atb_raddr == 7'd32);
	assign atb_lim[1] = (atb_raddr == 7'd127);
	assign atb_miss = atb_up? atb_lim[1] : atb_lim[0];

	// split atb pair into fields;
	// perm: 00=error, 01=read, 10=write;
	// both atb entries must be valid;

	wire [15:0] atb0_vaddr;		// virtual block address;
	wire [15:0] atb0_paddr;		// physical block address;
	wire [3:0] atb0_size;		// size of region;
	wire [1:0] atb0_perm;		// access rights;
	wire [1:0] atb0_dev;		// target device;
	wire atb0_iv;				// enable pio access;

	wire [15:0] atb1_vaddr;		// virtual block address;
	wire [15:0] atb1_paddr;		// physical block address;
	wire [3:0] atb1_size;		// size of region;
	wire [1:0] atb1_perm;		// access rights;
	wire [1:0] atb1_dev;		// target device;
	wire atb1_iv;				// enable pio access;

	assign atb0_vaddr = atb_h[15:0];
	assign atb0_paddr = atb_h[31:16];
	assign atb0_size = atb_h[35:32];
	assign atb0_perm = atb_h[37:36];
	assign atb0_dev = atb_h[39:38];
	assign atb0_iv = atb_h[40];

	assign atb1_vaddr = atb_l[15:0];
	assign atb1_paddr = atb_l[31:16];
	assign atb1_size = atb_l[35:32];
	assign atb1_perm = atb_l[37:36];
	assign atb1_dev = atb_l[39:38];
	assign atb1_iv = atb_l[40];

	// decode size field into address compare mask;
	// block size is (16k << size);

	reg [29:14] atb0_mask;		// address comparison mask;
	reg [29:14] atb1_mask;		// address comparison mask;

	always @(atb0_size)
	begin
		case(atb0_size)
			4'h0: atb0_mask <= 16'h0000;
			4'h1: atb0_mask <= 16'h0001;
			4'h2: atb0_mask <= 16'h0003;
			4'h3: atb0_mask <= 16'h0007;
			4'h4: atb0_mask <= 16'h000f;
			4'h5: atb0_mask <= 16'h001f;
			4'h6: atb0_mask <= 16'h003f;
			4'h7: atb0_mask <= 16'h007f;
			4'h8: atb0_mask <= 16'h00ff;
			4'h9: atb0_mask <= 16'h01ff;
			4'ha: atb0_mask <= 16'h03ff;
			4'hb: atb0_mask <= 16'h07ff;
			4'hc: atb0_mask <= 16'h0fff;
			4'hd: atb0_mask <= 16'h1fff;
			4'he: atb0_mask <= 16'h3fff;
			4'hf: atb0_mask <= 16'h7fff;
		endcase
	end

	always @(atb1_size)
	begin
		case(atb1_size)
			4'h0: atb1_mask <= 16'h0000;
			4'h1: atb1_mask <= 16'h0001;
			4'h2: atb1_mask <= 16'h0003;
			4'h3: atb1_mask <= 16'h0007;
			4'h4: atb1_mask <= 16'h000f;
			4'h5: atb1_mask <= 16'h001f;
			4'h6: atb1_mask <= 16'h003f;
			4'h7: atb1_mask <= 16'h007f;
			4'h8: atb1_mask <= 16'h00ff;
			4'h9: atb1_mask <= 16'h01ff;
			4'ha: atb1_mask <= 16'h03ff;
			4'hb: atb1_mask <= 16'h07ff;
			4'hc: atb1_mask <= 16'h0fff;
			4'hd: atb1_mask <= 16'h1fff;
			4'he: atb1_mask <= 16'h3fff;
			4'hf: atb1_mask <= 16'h7fff;
		endcase
	end

	// compare atb entry with device address;
	// pi buffer holds two entries per address;
	// do xor comparisons while mask is computed;
	// match aborts search;
	// greater-than comparator determines up/down direction;

	wire [29:14] atb0_cmp;		// compare partials;
	wire [29:14] atb1_cmp;		// compare partials;
	wire atb_gt;				// virtual address greater than last;
	reg [1:0] atb_eq;			// matching atb entries;
	reg [29:14] atb0_phys;		// physical block;
	reg [29:14] atb1_phys;		// physical block;
	reg [29:14] atb_paddr;		// mapped physical block;
	reg [1:0] atb0_pdev;		// physical device;
	reg [1:0] atb1_pdev;		// physical device;
	reg [1:0] atb_iv;			// iv buffer;
	reg [1:0] atb_pdev;			// mapped physical device;
	reg atb_piv;				// iv mapping;
	wire [1:0] atb_viol;		// atb access violation;
	reg [1:0] atb_fault;		// needed in next clock;

	assign atb0_cmp = (atb_vaddr[29:14] ~^ atb0_vaddr) | atb0_mask;
	assign atb1_cmp = (atb_vaddr[29:14] ~^ atb1_vaddr) | atb1_mask;
	assign atb_gt = (atb_vaddr[29:14] > atb0_vaddr);

	assign atb_viol[0] = atb_pio? ~atb0_perm[1] : ~atb0_perm[0];
	assign atb_viol[1] = atb_pio? ~atb1_perm[1] : ~atb1_perm[0];

	always @(posedge sysclk)
	begin
		atb_eq[0] <= atb_pipe[2] & (&atb0_cmp);
		atb_eq[1] <= atb_pipe[2] & (&atb1_cmp);
		atb_up <= atb_gt;
		if(atb_pipe[2]) begin
			atb0_phys <= atb0_paddr + (atb_vaddr[29:14] & atb0_mask);
			atb1_phys <= atb1_paddr + (atb_vaddr[29:14] & atb1_mask);
			atb0_pdev <= atb0_dev;
			atb1_pdev <= atb1_dev;
			atb_iv <= { atb1_iv, atb0_iv };
			atb_fault <= atb_viol;
		end
		if(atb_update) begin
			atb_paddr <= ~atb_map? atb_vaddr : atb_eq[0]? atb0_phys : atb1_phys;
			atb_pdev <= ~atb_map? atb_udev : atb_eq[0]? atb0_pdev : atb1_pdev;
			atb_piv <= ~atb_map? 1'b0 : atb_eq[0]? atb_iv[0] : atb_iv[1];
			atb_cache <= atb_vaddr;
		end
	end

	assign atb_match = |atb_eq;
	assign atb_update = atb_match | atb_pass;

	// atb error handling;
	// error when atb miss;
	// error when access rights in atb don't match access type;
	// clear error on new atb lookup and reset;

	reg atb_err;			// atb mapping/access error;
	wire atb_err_clr;		// clear atb error bit;
	wire atb_err_acc;		// atb access error;
	wire atb_err_set;		// sum of all atb errors;

	assign atb_err_clr = atb_reset | atb_go;
	assign atb_err_acc = atb_eq[0]? atb_fault[0] : atb_fault[1];
	assign atb_err_set = atb_pipe[3] & (atb_match? atb_err_acc : atb_miss);

	always @(posedge sysclk)
	begin
		atb_err <= ~atb_err_clr & (atb_err | atb_err_set);
	end

endmodule