Johannes Schlatow
Published

Taking control over DMA transactions on Zynq with Genode

I implemented an IP core that restricts DMA transactions to certain addresses and integrated support for it into the Genode OS Framework.

AdvancedProtip3 hours389
Taking control over DMA transactions on Zynq with Genode

Things used in this project

Story

Read more

Code

dma_guard_v1_0.v

Verilog
`timescale 1 ns / 1 ps

	module dma_guard_v1_0 #
	(
		// Users to add parameters here

		// User parameters ends
		// Do not modify the parameters beyond this line


		// Parameters of Axi Master Bus Interface M00_AXI
		parameter integer C_M00_AXI_ID_WIDTH	= 1,
		parameter integer C_M00_AXI_ADDR_WIDTH	= 32,
		parameter integer C_M00_AXI_DATA_WIDTH	= 32,
		parameter integer C_M00_AXI_AWUSER_WIDTH	= 0,
		parameter integer C_M00_AXI_ARUSER_WIDTH	= 0,
		parameter integer C_M00_AXI_WUSER_WIDTH	= 0,
		parameter integer C_M00_AXI_RUSER_WIDTH	= 0,
		parameter integer C_M00_AXI_BUSER_WIDTH	= 0,

		// Parameters of Axi Slave Bus Interface S00_AXI
		parameter integer C_S00_AXI_ID_WIDTH	= 1,
		parameter integer C_S00_AXI_DATA_WIDTH	= 32,
		parameter integer C_S00_AXI_ADDR_WIDTH	= 32,
		parameter integer C_S00_AXI_AWUSER_WIDTH	= 0,
		parameter integer C_S00_AXI_ARUSER_WIDTH	= 0,
		parameter integer C_S00_AXI_WUSER_WIDTH	= 0,
		parameter integer C_S00_AXI_RUSER_WIDTH	= 0,
		parameter integer C_S00_AXI_BUSER_WIDTH	= 0,

		// Parameters of Axi Slave Bus Interface s_axi_control
		parameter integer C_s_axi_control_DATA_WIDTH	= 32,
		parameter integer C_s_axi_control_ADDR_WIDTH	= 6
	)
	(
		// Users to add ports here
		
		input wire  axi_aclk,
		input wire  axi_aresetn,

		// User ports ends
		// Do not modify the ports beyond this line


		// Ports of Axi Master Bus Interface M00_AXI
		output wire [C_M00_AXI_ID_WIDTH-1 : 0] m00_axi_awid,
		output wire [C_M00_AXI_ADDR_WIDTH-1 : 0] m00_axi_awaddr,
		output wire [7 : 0] m00_axi_awlen,
		output wire [2 : 0] m00_axi_awsize,
		output wire [1 : 0] m00_axi_awburst,
		output wire  m00_axi_awlock,
		output wire [3 : 0] m00_axi_awcache,
		output wire [2 : 0] m00_axi_awprot,
		output wire [3 : 0] m00_axi_awqos,
		output wire [C_M00_AXI_AWUSER_WIDTH-1 : 0] m00_axi_awuser,
		output wire  m00_axi_awvalid,
		input wire  m00_axi_awready,
		output wire [C_M00_AXI_DATA_WIDTH-1 : 0] m00_axi_wdata,
		output wire [C_M00_AXI_DATA_WIDTH/8-1 : 0] m00_axi_wstrb,
		output wire  m00_axi_wlast,
		output wire [C_M00_AXI_WUSER_WIDTH-1 : 0] m00_axi_wuser,
		output wire  m00_axi_wvalid,
		input wire  m00_axi_wready,
		input wire [C_M00_AXI_ID_WIDTH-1 : 0] m00_axi_bid,
		input wire [1 : 0] m00_axi_bresp,
		input wire [C_M00_AXI_BUSER_WIDTH-1 : 0] m00_axi_buser,
		input wire  m00_axi_bvalid,
		output wire  m00_axi_bready,
		output wire [C_M00_AXI_ID_WIDTH-1 : 0] m00_axi_arid,
		output wire [C_M00_AXI_ADDR_WIDTH-1 : 0] m00_axi_araddr,
		output wire [7 : 0] m00_axi_arlen,
		output wire [2 : 0] m00_axi_arsize,
		output wire [1 : 0] m00_axi_arburst,
		output wire  m00_axi_arlock,
		output wire [3 : 0] m00_axi_arcache,
		output wire [2 : 0] m00_axi_arprot,
		output wire [3 : 0] m00_axi_arqos,
		output wire [C_M00_AXI_ARUSER_WIDTH-1 : 0] m00_axi_aruser,
		output wire  m00_axi_arvalid,
		input wire  m00_axi_arready,
		input wire [C_M00_AXI_ID_WIDTH-1 : 0] m00_axi_rid,
		input wire [C_M00_AXI_DATA_WIDTH-1 : 0] m00_axi_rdata,
		input wire [1 : 0] m00_axi_rresp,
		input wire  m00_axi_rlast,
		input wire [C_M00_AXI_RUSER_WIDTH-1 : 0] m00_axi_ruser,
		input wire  m00_axi_rvalid,
		output wire  m00_axi_rready,

		// Ports of Axi Slave Bus Interface S00_AXI
		input wire [C_S00_AXI_ID_WIDTH-1 : 0] s00_axi_awid,
		input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_awaddr,
		input wire [7 : 0] s00_axi_awlen,
		input wire [2 : 0] s00_axi_awsize,
		input wire [1 : 0] s00_axi_awburst,
		input wire  s00_axi_awlock,
		input wire [3 : 0] s00_axi_awcache,
		input wire [2 : 0] s00_axi_awprot,
		input wire [3 : 0] s00_axi_awqos,
		input wire [3 : 0] s00_axi_awregion,
		input wire [C_S00_AXI_AWUSER_WIDTH-1 : 0] s00_axi_awuser,
		input wire  s00_axi_awvalid,
		output wire  s00_axi_awready,
		input wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_wdata,
		input wire [(C_S00_AXI_DATA_WIDTH/8)-1 : 0] s00_axi_wstrb,
		input wire  s00_axi_wlast,
		input wire [C_S00_AXI_WUSER_WIDTH-1 : 0] s00_axi_wuser,
		input wire  s00_axi_wvalid,
		output wire  s00_axi_wready,
		output wire [C_S00_AXI_ID_WIDTH-1 : 0] s00_axi_bid,
		output wire [1 : 0] s00_axi_bresp,
		output wire [C_S00_AXI_BUSER_WIDTH-1 : 0] s00_axi_buser,
		output wire  s00_axi_bvalid,
		input wire  s00_axi_bready,
		input wire [C_S00_AXI_ID_WIDTH-1 : 0] s00_axi_arid,
		input wire [C_S00_AXI_ADDR_WIDTH-1 : 0] s00_axi_araddr,
		input wire [7 : 0] s00_axi_arlen,
		input wire [2 : 0] s00_axi_arsize,
		input wire [1 : 0] s00_axi_arburst,
		input wire  s00_axi_arlock,
		input wire [3 : 0] s00_axi_arcache,
		input wire [2 : 0] s00_axi_arprot,
		input wire [3 : 0] s00_axi_arqos,
		input wire [3 : 0] s00_axi_arregion,
		input wire [C_S00_AXI_ARUSER_WIDTH-1 : 0] s00_axi_aruser,
		input wire  s00_axi_arvalid,
		output wire  s00_axi_arready,
		output wire [C_S00_AXI_ID_WIDTH-1 : 0] s00_axi_rid,
		output wire [C_S00_AXI_DATA_WIDTH-1 : 0] s00_axi_rdata,
		output wire [1 : 0] s00_axi_rresp,
		output wire  s00_axi_rlast,
		output wire [C_S00_AXI_RUSER_WIDTH-1 : 0] s00_axi_ruser,
		output wire  s00_axi_rvalid,
		input wire  s00_axi_rready,

		// Ports of Axi Slave Bus Interface s_axi_control
		input wire  s_axi_control_aclk,
		input wire  s_axi_control_aresetn,
		input wire [C_s_axi_control_ADDR_WIDTH-1 : 0] s_axi_control_awaddr,
		input wire [2 : 0] s_axi_control_awprot,
		input wire  s_axi_control_awvalid,
		output wire  s_axi_control_awready,
		input wire [C_s_axi_control_DATA_WIDTH-1 : 0] s_axi_control_wdata,
		input wire [(C_s_axi_control_DATA_WIDTH/8)-1 : 0] s_axi_control_wstrb,
		input wire  s_axi_control_wvalid,
		output wire  s_axi_control_wready,
		output wire [1 : 0] s_axi_control_bresp,
		output wire  s_axi_control_bvalid,
		input wire  s_axi_control_bready,
		input wire [C_s_axi_control_ADDR_WIDTH-1 : 0] s_axi_control_araddr,
		input wire [2 : 0] s_axi_control_arprot,
		input wire  s_axi_control_arvalid,
		output wire  s_axi_control_arready,
		output wire [C_s_axi_control_DATA_WIDTH-1 : 0] s_axi_control_rdata,
		output wire [1 : 0] s_axi_control_rresp,
		output wire  s_axi_control_rvalid,
		input wire  s_axi_control_rready
	);
	
	// signals passed to s_axi_control module
	wire ar_allowed;
	wire aw_allowed;
	wire [14 : 0] rd_len;
	wire [14 : 0] wr_len;
	
	assign rd_len = s00_axi_arlen << s00_axi_arsize;
	assign wr_len = s00_axi_awlen << s00_axi_awsize;

// Instantiation of Axi Bus Interface s_axi_control
	dma_guard_v1_0_s_axi_control # ( 
		.C_S_AXI_DATA_WIDTH(C_s_axi_control_DATA_WIDTH),
		.C_S_AXI_ADDR_WIDTH(C_s_axi_control_ADDR_WIDTH)
	) dma_guard_v1_0_s_axi_control_inst (
		.rd_allowed(ar_allowed),
		.rd_addr(s00_axi_araddr),
		.rd_len(rd_len),
		.wr_allowed(aw_allowed),
		.wr_addr(s00_axi_awaddr),
		.wr_len(wr_len),
		.S_AXI_ACLK(s_axi_control_aclk),
		.S_AXI_ARESETN(s_axi_control_aresetn),
		.S_AXI_AWADDR(s_axi_control_awaddr),
		.S_AXI_AWPROT(s_axi_control_awprot),
		.S_AXI_AWVALID(s_axi_control_awvalid),
		.S_AXI_AWREADY(s_axi_control_awready),
		.S_AXI_WDATA(s_axi_control_wdata),
		.S_AXI_WSTRB(s_axi_control_wstrb),
		.S_AXI_WVALID(s_axi_control_wvalid),
		.S_AXI_WREADY(s_axi_control_wready),
		.S_AXI_BRESP(s_axi_control_bresp),
		.S_AXI_BVALID(s_axi_control_bvalid),
		.S_AXI_BREADY(s_axi_control_bready),
		.S_AXI_ARADDR(s_axi_control_araddr),
		.S_AXI_ARPROT(s_axi_control_arprot),
		.S_AXI_ARVALID(s_axi_control_arvalid),
		.S_AXI_ARREADY(s_axi_control_arready),
		.S_AXI_RDATA(s_axi_control_rdata),
		.S_AXI_RRESP(s_axi_control_rresp),
		.S_AXI_RVALID(s_axi_control_rvalid),
		.S_AXI_RREADY(s_axi_control_rready)
	);

	// Add user logic here 
	
	// signals for read-transfer emulation/interception
	reg axi_arvalid;
	reg axi_arready;
	reg axi_rvalid;
	reg axi_rresp;
	reg axi_rlast;
	reg [1:0] read_state;
	
	// signals for write-transfer emulation/interception
	reg axi_awvalid;
	reg axi_awready;
	reg axi_wvalid;
	reg axi_wready;
	reg axi_bresp;
	reg axi_bvalid;
	reg [1:0] write_state;

	// read/write state values
	localparam [1:0]
	   FORWARD = 2'b00,
	   READY   = 2'b01,
	   WAIT    = 2'b10,
	   RESP    = 2'b11;
	
	// read response values
	localparam [1:0]
	   OKAY    = 2'b00,
	   EXOKAY  = 2'b01,
	   SLVERR  = 2'b10,
	   DECERR  = 2'b11;

	assign m00_axi_awid      = s00_axi_awid;
	assign m00_axi_awaddr    = s00_axi_awaddr;
	assign m00_axi_awlen     = s00_axi_awlen;
	assign m00_axi_awsize    = s00_axi_awsize;
	assign m00_axi_awburst   = s00_axi_awburst;
	assign m00_axi_awlock    = s00_axi_awlock;
	assign m00_axi_awcache   = s00_axi_awcache;
	assign m00_axi_awprot    = s00_axi_awprot;
	assign m00_axi_awqos     = s00_axi_awqos;
	assign m00_axi_awuser    = s00_axi_awuser;
	assign m00_axi_awvalid   = write_state == FORWARD && aw_allowed ? s00_axi_awvalid : axi_awvalid;
	assign s00_axi_awready   = write_state == FORWARD && aw_allowed ? m00_axi_awready : axi_awready;
	assign m00_axi_wdata     = s00_axi_wdata;
	assign m00_axi_wstrb     = s00_axi_wstrb;
	assign m00_axi_wlast     = s00_axi_wlast;
	assign m00_axi_wuser     = s00_axi_wuser;
	assign m00_axi_wvalid    = write_state == FORWARD && aw_allowed ? s00_axi_wvalid : axi_wvalid;
	assign s00_axi_wready    = write_state == FORWARD && aw_allowed ? m00_axi_wready : axi_wready;
	assign s00_axi_bid       = m00_axi_bid;
	assign s00_axi_bresp     = write_state == RESP ? axi_bresp : m00_axi_bresp;
	assign s00_axi_buser     = m00_axi_buser;
	assign s00_axi_bvalid    = write_state == RESP ? axi_bvalid : m00_axi_bvalid;
	assign m00_axi_bready    = s00_axi_bready;

	assign m00_axi_arid      = s00_axi_arid;
	assign m00_axi_araddr    = s00_axi_araddr;
	assign m00_axi_arlen     = s00_axi_arlen;
	assign m00_axi_arsize    = s00_axi_arsize;
	assign m00_axi_arburst   = s00_axi_arburst;
	assign m00_axi_arlock    = s00_axi_arlock;
	assign m00_axi_arcache   = s00_axi_arcache;
	assign m00_axi_arprot    = s00_axi_arprot;
	assign m00_axi_arqos     = s00_axi_arqos;
	assign m00_axi_aruser    = s00_axi_aruser;
	assign m00_axi_arvalid   = read_state == FORWARD && ar_allowed == 1 ? s00_axi_arvalid : axi_arvalid;
	assign s00_axi_arready   = read_state == FORWARD && ar_allowed == 1 ? m00_axi_arready : axi_arready;
	assign s00_axi_rid       = m00_axi_rid;
	assign s00_axi_rdata     = m00_axi_rdata;
	assign s00_axi_rresp     = read_state == RESP ? axi_rresp : m00_axi_rresp;
	assign s00_axi_rlast     = read_state == RESP ? axi_rlast : m00_axi_rlast;
	assign s00_axi_ruser     = m00_axi_ruser;
	assign s00_axi_rvalid    = read_state == RESP ? axi_rvalid : m00_axi_rvalid;
	assign m00_axi_rready    = s00_axi_rready;

	// implement clock-synchronous write signal updates
	always @(posedge axi_aclk)
	begin
		if (axi_aresetn == 1'b0)
			begin
				write_state  <= FORWARD;
				axi_awvalid <= 1'b0;
				axi_awready <= 1'b0;
				axi_wvalid  <= 1'b0;
				axi_wready  <= 1'b0;
				axi_bresp   <= 1'b0;
				axi_bvalid  <= 1'b0;
			end
		else
			begin
				case (write_state)
					READY: begin
						if (axi_awready && s00_axi_awvalid)
							begin
								// deassert ready signal to stop accepting write requests
								axi_awready <= 1'b0;
								// assert the wready signal to accept data
								axi_wready <= 1'b1;
								write_state <= WAIT;
							end
					end
					WAIT: begin
						// wait for last wdata
						if (s00_axi_wvalid && s00_axi_wlast)
							begin
								write_state <= RESP;
								axi_wready <= 1'b0;
								axi_bvalid <= 1'b0;
							end
					end
					RESP: begin
						// insert our own response
						if (~axi_bvalid)
							begin
								axi_bresp  <= DECERR;
								axi_bvalid <= 1'b1;
							end
						else if (s00_axi_bready)
							begin
								axi_bvalid <= 1'b0;
								axi_bresp  <= 1'b0;
								write_state <= FORWARD;
							end
					end
					FORWARD: begin
						// master tries issueing an invalid transfer
						if (~aw_allowed && s00_axi_awvalid)
							begin
								// don't forward valid signal because we are blocking the transfer
								axi_awvalid <= 1'b0;
								// yet, accept address
								axi_awready <= 1'b1;
								write_state <= READY;
							end
						else
							begin
								axi_awvalid <= 1'b0;
								axi_awready <= 1'b0;
								axi_wvalid  <= 1'b0;
								axi_wready  <= 1'b0;
								axi_bresp   <= 1'b0;
								axi_bvalid  <= 1'b0;
							end
					end
					default: begin
						write_state <= FORWARD;
					end
				endcase
			end
	end
	
	// implement clock-synchronous read signal updates
	always @(posedge axi_aclk)
	begin
		if (axi_aresetn == 1'b0)
			begin
				read_state  <= FORWARD;
				axi_arvalid <= 1'b0;
				axi_arready <= 1'b0;
				axi_rvalid  <= 1'b0;
				axi_rresp   <= 1'b0;
				axi_rlast   <= 1'b0;
			end
		else
			begin
				case (read_state)
					READY: begin
						if (axi_arready && s00_axi_arvalid)
							begin
								// deassert ready signal to finish read request 
								axi_arready <= 1'b0;
								read_state <= WAIT;
							end
					end
					WAIT: begin
						// wait for m00 read channel to become silent
						if (~m00_axi_rvalid)
							begin
								read_state <= RESP;
								axi_rvalid <= 1'b0;
							end
					end
					RESP: begin
						// insert our own response
						if (~axi_rvalid)
							begin
								axi_rresp  <= DECERR;
								axi_rvalid <= 1'b1;
								axi_rlast  <= 1'b1;
							end
						else if (s00_axi_rready)
							begin
								axi_rvalid <= 1'b0;
								axi_rlast  <= 1'b0;
								read_state <= FORWARD;
							end
					end
					FORWARD: begin
						// master tries issueing an invalid transfer
						if (~ar_allowed && s00_axi_arvalid)
							begin
								// don't forward valid signal because we are blocking the transfer
								axi_arvalid <= 1'b0;
								// yet, accept address
								axi_arready <= 1'b1;
								read_state <= READY;
							end
						else
							begin
								axi_arvalid <= 1'b0;
								axi_arready <= 1'b0;
								axi_rvalid  <= 1'b0;
								axi_rresp   <= 1'b0;
								axi_rlast   <= 1'b0;
							end
					end
					default: begin
						read_state <= FORWARD;
					end
				endcase
			end
	end
	
	// User logic ends

	endmodule

dma_guard_v1_0_s_axi_control.v

Verilog
`timescale 1 ns / 1 ps

	module dma_guard_v1_0_s_axi_control #
	(
		// Users to add parameters here
		
		parameter integer RD_ADDR_WIDTH = 32,
		parameter integer WR_ADDR_WIDTH = 32,

		// User parameters ends
		// Do not modify the parameters beyond this line

		// Width of S_AXI data bus
		parameter integer C_S_AXI_DATA_WIDTH	= 32,
		// Width of S_AXI address bus
		parameter integer C_S_AXI_ADDR_WIDTH	= 6
	)
	(
		// Users to add ports here
		
		output wire rd_allowed,
		input  wire [RD_ADDR_WIDTH-1:0] rd_addr,
		input  wire [7:0] rd_len,
		output wire wr_allowed,
		input  wire [WR_ADDR_WIDTH-1:0] wr_addr,
		input  wire [7:0] wr_len,

		// User ports ends
		// Do not modify the ports beyond this line

		// Global Clock Signal
		input wire  S_AXI_ACLK,
		// Global Reset Signal. This Signal is Active LOW
		input wire  S_AXI_ARESETN,
		// Write address (issued by master, acceped by Slave)
		input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
		// Write channel Protection type. This signal indicates the
    		// privilege and security level of the transaction, and whether
    		// the transaction is a data access or an instruction access.
		input wire [2 : 0] S_AXI_AWPROT,
		// Write address valid. This signal indicates that the master signaling
    		// valid write address and control information.
		input wire  S_AXI_AWVALID,
		// Write address ready. This signal indicates that the slave is ready
    		// to accept an address and associated control signals.
		output wire  S_AXI_AWREADY,
		// Write data (issued by master, acceped by Slave) 
		input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
		// Write strobes. This signal indicates which byte lanes hold
    		// valid data. There is one write strobe bit for each eight
    		// bits of the write data bus.    
		input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
		// Write valid. This signal indicates that valid write
    		// data and strobes are available.
		input wire  S_AXI_WVALID,
		// Write ready. This signal indicates that the slave
    		// can accept the write data.
		output wire  S_AXI_WREADY,
		// Write response. This signal indicates the status
    		// of the write transaction.
		output wire [1 : 0] S_AXI_BRESP,
		// Write response valid. This signal indicates that the channel
    		// is signaling a valid write response.
		output wire  S_AXI_BVALID,
		// Response ready. This signal indicates that the master
    		// can accept a write response.
		input wire  S_AXI_BREADY,
		// Read address (issued by master, acceped by Slave)
		input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
		// Protection type. This signal indicates the privilege
    		// and security level of the transaction, and whether the
    		// transaction is a data access or an instruction access.
		input wire [2 : 0] S_AXI_ARPROT,
		// Read address valid. This signal indicates that the channel
    		// is signaling valid read address and control information.
		input wire  S_AXI_ARVALID,
		// Read address ready. This signal indicates that the slave is
    		// ready to accept an address and associated control signals.
		output wire  S_AXI_ARREADY,
		// Read data (issued by slave)
		output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
		// Read response. This signal indicates the status of the
    		// read transfer.
		output wire [1 : 0] S_AXI_RRESP,
		// Read valid. This signal indicates that the channel is
    		// signaling the required read data.
		output wire  S_AXI_RVALID,
		// Read ready. This signal indicates that the master can
    		// accept the read data and response information.
		input wire  S_AXI_RREADY
	);

	// AXI4LITE signals
	reg [C_S_AXI_ADDR_WIDTH-1 : 0] 	axi_awaddr;
	reg  	axi_awready;
	reg  	axi_wready;
	reg [1 : 0] 	axi_bresp;
	reg  	axi_bvalid;
	reg [C_S_AXI_ADDR_WIDTH-1 : 0] 	axi_araddr;
	reg  	axi_arready;
	reg [C_S_AXI_DATA_WIDTH-1 : 0] 	axi_rdata;
	reg [1 : 0] 	axi_rresp;
	reg  	axi_rvalid;

	// Example-specific design signals
	// local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
	// ADDR_LSB is used for addressing 32/64 bit registers/memories
	// ADDR_LSB = 2 for 32 bits (n downto 2)
	// ADDR_LSB = 3 for 64 bits (n downto 3)
	localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
	localparam integer OPT_MEM_ADDR_BITS = 3;
	//----------------------------------------------
	//-- Signals for user logic register space example
	//------------------------------------------------
	//-- Number of Slave Registers 11
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg0;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg1;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg2;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg3;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg4;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg5;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg6;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg7;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg8;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg9;
	reg [C_S_AXI_DATA_WIDTH-1:0]	slv_reg10;
	wire	 slv_reg_rden;
	wire	 slv_reg_wren;
	reg [C_S_AXI_DATA_WIDTH-1:0]	 reg_data_out;
	integer	 byte_index;
	reg	 aw_en;

	// I/O Connections assignments

	assign S_AXI_AWREADY	= axi_awready;
	assign S_AXI_WREADY	= axi_wready;
	assign S_AXI_BRESP	= axi_bresp;
	assign S_AXI_BVALID	= axi_bvalid;
	assign S_AXI_ARREADY	= axi_arready;
	assign S_AXI_RDATA	= axi_rdata;
	assign S_AXI_RRESP	= axi_rresp;
	assign S_AXI_RVALID	= axi_rvalid;
	// Implement axi_awready generation
	// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
	// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
	// de-asserted when reset is low.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_awready <= 1'b0;
	      aw_en <= 1'b1;
	    end 
	  else
	    begin    
	      if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
	        begin
	          // slave is ready to accept write address when 
	          // there is a valid write address and write data
	          // on the write address and data bus. This design 
	          // expects no outstanding transactions. 
	          axi_awready <= 1'b1;
	          aw_en <= 1'b0;
	        end
	        else if (S_AXI_BREADY && axi_bvalid)
	            begin
	              aw_en <= 1'b1;
	              axi_awready <= 1'b0;
	            end
	      else           
	        begin
	          axi_awready <= 1'b0;
	        end
	    end 
	end       

	// Implement axi_awaddr latching
	// This process is used to latch the address when both 
	// S_AXI_AWVALID and S_AXI_WVALID are valid. 

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_awaddr <= 0;
	    end 
	  else
	    begin    
	      if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
	        begin
	          // Write Address latching 
	          axi_awaddr <= S_AXI_AWADDR;
	        end
	    end 
	end       

	// Implement axi_wready generation
	// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
	// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is 
	// de-asserted when reset is low. 

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_wready <= 1'b0;
	    end 
	  else
	    begin    
	      if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
	        begin
	          // slave is ready to accept write data when 
	          // there is a valid write address and write data
	          // on the write address and data bus. This design 
	          // expects no outstanding transactions. 
	          axi_wready <= 1'b1;
	        end
	      else
	        begin
	          axi_wready <= 1'b0;
	        end
	    end 
	end       

	// Implement memory mapped register select and write logic generation
	// The write data is accepted and written to memory mapped registers when
	// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
	// select byte enables of slave registers while writing.
	// These registers are cleared when reset (active low) is applied.
	// Slave register write enable is asserted when valid address and data are available
	// and the slave is ready to accept the write address and write data.
	assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      slv_reg0 <= 0;
	      slv_reg1 <= 0;
	      slv_reg2 <= 0;
	      slv_reg3 <= 0;
	      slv_reg4 <= 0;
	      slv_reg5 <= 0;
	      slv_reg6 <= 0;
	      slv_reg7 <= 0;
	      slv_reg8 <= 0;
	      slv_reg9 <= 0;
	      slv_reg10 <= 0;
	    end 
	  else begin
	    if (slv_reg_wren)
	      begin
	        case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
	          4'h0:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 0
	                slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h1:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 1
	                slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h2:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 2
	                slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h3:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 3
	                slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h4:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 4
	                slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h5:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 5
	                slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h6:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 6
	                slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h7:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 7
	                slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h8:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 8
	                slv_reg8[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'h9:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 9
	                slv_reg9[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          4'hA:
	            for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
	              if ( S_AXI_WSTRB[byte_index] == 1 ) begin
	                // Respective byte enables are asserted as per write strobes 
	                // Slave register 10
	                slv_reg10[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
	              end  
	          default : begin
	                      slv_reg0 <= slv_reg0;
	                      slv_reg1 <= slv_reg1;
	                      slv_reg2 <= slv_reg2;
	                      slv_reg3 <= slv_reg3;
	                      slv_reg4 <= slv_reg4;
	                      slv_reg5 <= slv_reg5;
	                      slv_reg6 <= slv_reg6;
	                      slv_reg7 <= slv_reg7;
	                      slv_reg8 <= slv_reg8;
	                      slv_reg9 <= slv_reg9;
	                      slv_reg10 <= slv_reg10;
	                    end
	        endcase
	      end
	  end
	end    

	// Implement write response logic generation
	// The write response and response valid signals are asserted by the slave 
	// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.  
	// This marks the acceptance of address and indicates the status of 
	// write transaction.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_bvalid  <= 0;
	      axi_bresp   <= 2'b0;
	    end 
	  else
	    begin    
	      if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
	        begin
	          // indicates a valid write response is available
	          axi_bvalid <= 1'b1;
	          axi_bresp  <= 2'b0; // 'OKAY' response 
	        end                   // work error responses in future
	      else
	        begin
	          if (S_AXI_BREADY && axi_bvalid) 
	            //check if bready is asserted while bvalid is high) 
	            //(there is a possibility that bready is always asserted high)   
	            begin
	              axi_bvalid <= 1'b0; 
	            end  
	        end
	    end
	end   

	// Implement axi_arready generation
	// axi_arready is asserted for one S_AXI_ACLK clock cycle when
	// S_AXI_ARVALID is asserted. axi_awready is 
	// de-asserted when reset (active low) is asserted. 
	// The read address is also latched when S_AXI_ARVALID is 
	// asserted. axi_araddr is reset to zero on reset assertion.

	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_arready <= 1'b0;
	      axi_araddr  <= 32'b0;
	    end 
	  else
	    begin    
	      if (~axi_arready && S_AXI_ARVALID)
	        begin
	          // indicates that the slave has acceped the valid read address
	          axi_arready <= 1'b1;
	          // Read address latching
	          axi_araddr  <= S_AXI_ARADDR;
	        end
	      else
	        begin
	          axi_arready <= 1'b0;
	        end
	    end 
	end       

	// Implement axi_arvalid generation
	// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both 
	// S_AXI_ARVALID and axi_arready are asserted. The slave registers 
	// data are available on the axi_rdata bus at this instance. The 
	// assertion of axi_rvalid marks the validity of read data on the 
	// bus and axi_rresp indicates the status of read transaction.axi_rvalid 
	// is deasserted on reset (active low). axi_rresp and axi_rdata are 
	// cleared to zero on reset (active low).  
	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_rvalid <= 0;
	      axi_rresp  <= 0;
	    end 
	  else
	    begin    
	      if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
	        begin
	          // Valid read data is available at the read data bus
	          axi_rvalid <= 1'b1;
	          axi_rresp  <= 2'b0; // 'OKAY' response
	        end   
	      else if (axi_rvalid && S_AXI_RREADY)
	        begin
	          // Read data is accepted by the master
	          axi_rvalid <= 1'b0;
	        end                
	    end
	end    

	// Implement memory mapped register select and read logic generation
	// Slave register read enable is asserted when valid address is available
	// and the slave is ready to accept the read address.
	assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
	always @(*)
	begin
	      // Address decoding for reading registers
	      case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
	        4'h0   : reg_data_out <= slv_reg0;
	        4'h1   : reg_data_out <= slv_reg1;
	        4'h2   : reg_data_out <= slv_reg2;
	        4'h3   : reg_data_out <= slv_reg3;
	        4'h4   : reg_data_out <= slv_reg4;
	        4'h5   : reg_data_out <= slv_reg5;
	        4'h6   : reg_data_out <= slv_reg6;
	        4'h7   : reg_data_out <= slv_reg7;
	        4'h8   : reg_data_out <= slv_reg8;
	        4'h9   : reg_data_out <= slv_reg9;
	        4'hA   : reg_data_out <= slv_reg10;
	        default : reg_data_out <= 0;
	      endcase
	end

	// Output register or memory read data
	always @( posedge S_AXI_ACLK )
	begin
	  if ( S_AXI_ARESETN == 1'b0 )
	    begin
	      axi_rdata  <= 0;
	    end 
	  else
	    begin    
	      // When there is a valid read address (S_AXI_ARVALID) with 
	      // acceptance of read address by the slave (axi_arready), 
	      // output the read dada 
	      if (slv_reg_rden)
	        begin
	          axi_rdata <= reg_data_out;     // register read data
	        end   
	    end
	end    

	// Add user logic here
	
	function readable (input [31 : 0] cfg);
		readable = cfg[0];
	endfunction
	
	function writeable (input [31 : 0] cfg);
		writeable = cfg[0] & cfg[1];
	endfunction
	
	function [31 : 0] size (input [31 : 0] cfg);
		/* 2^(size+2) - 1 */
		size = cfg[8:4] >= 30 ? 32'hffffffff : (1 << cfg[8:4]+2)-1;
	endfunction
	
	function [31 : 0 ] start_addr (input [31 : 0] cfg);
		start_addr = (cfg[31:12] << 12) & ~size(cfg);
	endfunction
	
	function [31 : 0 ] end_addr (input [31 : 0] cfg);
		end_addr = start_addr(cfg) | size(cfg);
	endfunction
	
	function within_range;
		input [31 : 0] addr;
		input integer  len;
		input [31 : 0] start_addr;
		input [31 : 0] end_addr;
		begin
			within_range = addr >= start_addr && addr+len-1 <= end_addr;
		end
	endfunction
	
	wire [9:0] rd_valid;
	wire [9:0] wr_valid;
	wire [31 : 0] addr_regs [9:0];
	assign addr_regs[0] = slv_reg1;
	assign addr_regs[1] = slv_reg2;
	assign addr_regs[2] = slv_reg3;
	assign addr_regs[3] = slv_reg4;
	assign addr_regs[4] = slv_reg5;
	assign addr_regs[5] = slv_reg6;
	assign addr_regs[6] = slv_reg7;
	assign addr_regs[7] = slv_reg8;
	assign addr_regs[8] = slv_reg9;
	assign addr_regs[9] = slv_reg10;
	
	genvar i;
	generate
	   for (i=0; i < 10; i=i+1) begin
	      assign rd_valid[i] = readable(addr_regs[i])  && within_range(rd_addr, rd_len, start_addr(addr_regs[i]), end_addr(addr_regs[i]));
	      assign wr_valid[i] = writeable(addr_regs[i]) && within_range(wr_addr, wr_len, start_addr(addr_regs[i]), end_addr(addr_regs[i]));
	   end
	endgenerate
	
	assign rd_allowed = ~slv_reg0[0] && |rd_valid;
	assign wr_allowed = ~slv_reg0[1] && |wr_valid;

	// User logic ends

	endmodule

Genode repository

Genode Zynq support

Credits

Johannes Schlatow

Johannes Schlatow

5 projects • 5 followers
Johannes is a developer at Genode Labs and has a research background in embedded systems and real-time operating systems.

Comments