📄 spi4_tx.c
字号:
// Copyright (C) 2002-2003 Intel Corporation, All Rights Reserved.
// Permission is hereby granted to merge this program code with
// other program material to create a derivative work. This
// derivative work may be distributed in compiled object form only.
// Any other publication of this program, in any form, without the
// explicit permission of the copyright holder is prohibited.
//
// Send questions and comments to erik.j.johnson@intel.com,
// aaron.kunze@intel.com
//-------------------------------------------------------------------
// spi4_tx.c - Chapter 5
// Dequeues packets from processing, then segments them and transmits
// them using a single thread onto a single port
//
#include "ixp.h"
#include "spi4_tx.h"
#include "dl_buf.c"
#include "dl_meta.h"
#include "dl_source.h"
extern dl_buf_handle_t dlBufHandle;
extern __declspec(gp_reg) int dlNextBlock;
extern dl_meta_t dlMeta;
#define TBUF_ELEM_SIZE 64
#define TBUF_ELEM_SIZE_SHIFT 6
#define NUM_TBUFS (128 * 64 / TBUF_ELEM_SIZE)
/*#define TX_CONTROL_VAL ((1 << TX_EN_SPHY_BITPOS) | \
(1 << TX_ENABLE_BITPOS) | \
(0 << TBUF_ELE_SIZE_0_BITPOS))
*/
// VALUE FOR THE SPI3
#define TX_CONTROL_VAL ((0 << TX_EN_SPHY_BITPOS) | \
(0 << TBUF_ELE_SIZE_0_BITPOS) | \
(0 << TX_MPHY_LEVEL2) | \
(1 << TX_MPHY_EN) | \
(0 << TX_MPHY_MODE)| \
(0 << TX_MPHY_POLL_MODE) | \
(00 << TX_WIDTH) | \
(0 << TX_MODE))
#define TX_CONTROL_VAL_SECOND ((TX_CONTROL_VAL) | \
(1<< TX_EN_SPHY_BITPOS))
#define TX_UP_CONTROL_VAL ((1 << TX_UP_CONTROL_CPMODE) | \
(1 << TX_UP_CONTROL_PPMODE) | \
(1 << TX_UP_CONTROL_DRTIME))
//-------------------------------------------------------------------
// This state is used to store the current packet's segmentation
// state
typedef struct s_tx_state
{
// 1 if the current mpacket is SOP/EOP respectively
unsigned int sop, eop;
// A pointer to the current mpacket
__declspec(dram) unsigned char *cur_mpacket_addr;
// Length remaining, in bytes, of the current mpacket
unsigned int remaining_length;
// The handle of the current buffer
dl_buf_handle_t cur_buf_handle;
} tx_state_t;
static tx_state_t tx_state;
//-------------------------------------------------------------------
// spi4_tx_init
//
// Description:
// Initialize the appropriate CSRs to transmit packets on
// the SPI4 interface.
//
// Parameters:
// Outputs: n/a
// In/Outs: n/a
// Inputs: n/a
// Constants: n/a
// Labels: n/a
//
// Side effects: Writes to the MSF transmit control CSR
//
// See also: n/a
//
void spi4_tx_init()
{
void* addr; // Holds the address of the CSR being set
__declspec(sram_write_reg) unsigned int tx_ctl;
SIGNAL msf_tx_ctl_sig;
//----------- Set the receive control CSR in the MSF.
//可能要写一次tx_en在这里为0
tx_ctl = TX_CONTROL_VAL;
addr = (void *)MSF_TX_CONTROL_ADDR;
msf_write(&tx_ctl, addr, 1, ctx_swap, &msf_tx_ctl_sig);
//初始化以后要再写一次tx_en
tx_ctl= TX_UP_CONTROL_VAL;
addr = (void *)MSF_TX_UP_CONTROL_0_ADDR;
msf_write(&tx_ctl,addr,1,ctx_swap,&msf_tx_ctl_sig);
addr = (void *)MSF_TX_UP_CONTROL_1_ADDR;
msf_write(&tx_ctl,addr,1,ctx_swap,&msf_tx_ctl_sig);
addr = (void *)MSF_TX_UP_CONTROL_2_ADDR;
msf_write(&tx_ctl,addr,1,ctx_swap,&msf_tx_ctl_sig);
addr = (void *)MSF_TX_UP_CONTROL_3_ADDR;
msf_write(&tx_ctl,addr,1,ctx_swap,&msf_tx_ctl_sig);
addr = (void *)MSF_TX_CONTROL_ADDR;
tx_ctl=TX_CONTROL_VAL_SECOND;
msf_write(&tx_ctl,addr,1,ctx_swap,&msf_tx_ctl_sig);
}
//-------------------------------------------------------------------
// _spi4_tx_move_dram_to_tbuf
//
// Description:
// Transfer the given DRAM memory into the given TBUF element
//
// Parameters:
// Outputs: n/a
// In/Outs: n/a
// Inputs: in_tbuf_elem - The TBUF element number to use
// in_dram_addr - The start address to the packet to
// transfer
// in_size - The number of bytes to transfer
// in_dram_sig - The signal to use for the transfer
// Constants: n/a
// Labels: n/a
//
// Side effects: The given dual signal must be caught by the
// calling routine.
// The in_size must be between 1 and
// 128. The number of bytes transfered will
// be (in_size >> 3) << 3) since
// the transfer must be an even number of quadwords
// See also: n/a
//
__forceinline
static void _spi4_tx_move_dram_to_tbuf(
unsigned int in_tbuf_elem,
void __declspec(dram) *in_dram_addr,
unsigned int in_size,
SIGNAL_PAIR *in_dram_sig)
{
dram_rbuf_tbuf_ind_t indir;
unsigned int tbuf_addr;
// Compute the TBUF address. This is the base TBUF
// address in the MSF plus the element number times
// 64.
tbuf_addr = MSF_TBUF_BASE_ADDR +
(in_tbuf_elem << 6);
// Override the tbuf address
indir.value = 0;
indir.ov_buf_addr = 1;
indir.buf_addr = tbuf_addr;
// Override the transfer size
indir.ov_ref_count = 1;
indir.ref_count = ((in_size + 7) >> 3) - 1;
dram_tbuf_write_ind(in_dram_addr,
8,
indir,
sig_done,
in_dram_sig);
}
//-------------------------------------------------------------------
// _spi4_tx_validate_tbuf
//
// Description:
// Validate the given TBUF entry
//
// Parameters:
// Outputs: n/a
// In/Outs: n/a
// Inputs: in_tbuf_elem - The TBUF element number to use
// in_sop - 1 if the TBUF is an SOP, 0 otherwise
// in_eop - 1 if the TBUF is an EOP, 0 otherwise
// in_size - The number of bytes to transfer
// Constants: n/a
// Labels: n/a
//
// Side effects: The given signal must be caught by the
// calling routine.
// See also: n/a
//
__forceinline
static void _spi4_tx_validate_tbuf(
unsigned int in_tbuf_elem,
unsigned int in_sop,
unsigned int in_eop,
unsigned int in_size)
{
spi4_tcw_t tbuf_control;
__declspec(sram_write_reg) spi4_tcw_t tbuf_control_wr;
SIGNAL msf_sig;
tbuf_control.control_word.whole = 0;
tbuf_control.reserved = 0;
// Set the mpacket length
tbuf_control.control_word.parts.payload_length =
in_size;
// Set SOP and EOP
tbuf_control.control_word.parts.sop = in_sop;
tbuf_control.control_word.parts.eop = in_eop;
tbuf_control_wr = tbuf_control;
msf_write(
&tbuf_control_wr,
(void *)(MSF_TBUF_CONTROL_BASE_ADDR +
(in_tbuf_elem<<3)),
sizeof(tbuf_control_wr) / sizeof(unsigned int),
ctx_swap,
&msf_sig);
}
//-------------------------------------------------------------------
// _spi4_tx_update_tbufs_in_flight
//
// Description:
// Read the current TX sequence number and with the last
// sequence number value, update the number of tbuf
// elements in flight.
//
// Parameters:
// Outputs: n/a
// In/Outs: io_tbuf_in_flight - On input, the last number of
// TBUFs in flight based on
// the last read value of
// io_last_tx_seq. On output,
// the number of tbufs in flight
// according to the updated
// io_last_tx_seq
// io_last_tx_seq - On input, the last value
// read for the TX sequence number.
// On output, the current value
// of the TX sequence number
// Inputs: n/a
// Constants: n/a
// Labels: n/a
//
// Side effects: n/a
// See also: n/a
//
__forceinline
static void _spi4_tx_update_tbufs_in_flight(
unsigned int *io_tbufs_in_flight,
unsigned int *io_last_tx_seq)
{
unsigned int cur_tx_seq;
__declspec(sram_read_reg) unsigned int cur_tx_seq_rd;
unsigned int tbufs_used;
SIGNAL msf_sig;
// First read the current sequence number
msf_read(&cur_tx_seq_rd,
(void *)MSF_TX_SEQUENCE_0_ADDR,
sizeof(cur_tx_seq_rd) / sizeof(unsigned int),
ctx_swap,
&msf_sig);
cur_tx_seq = cur_tx_seq_rd & 0xff;
// Compute how many TBUFs have been consumed since
// the last read. Account for wrap around
if (*io_last_tx_seq <= cur_tx_seq)
{
tbufs_used = cur_tx_seq - *io_last_tx_seq;
}
else
{
tbufs_used = *io_last_tx_seq - cur_tx_seq;
}
// Subtract the tbufs_used from the current number of
// tbufs in flight
*io_tbufs_in_flight -= tbufs_used;
// Save the sequence number
*io_last_tx_seq = cur_tx_seq;
}
//-------------------------------------------------------------------
// _spi4_tx_get_and_update_state
//
// Description:
// Get and update the segmentation state associated with a
// particular TBUF element. This version of the routine first
// attempts to extract the state from global registers, if that
// state has the EOP state set, then a new packet is dequeued
// and the first mpacket for the new packet is returned.
//
// Parameters:
// Outputs: The handle of the current buffer,
// a pointer to the current mpacket
// to transmit,
// the length (in bytes) of the mpacket,
// sop and eop indicators.
// In/Outs: n/a
// Inputs: in_next_tbuf_elem - The TBUF element for which
// to retrieve the state
// Constants: n/a
// Labels: n/a
//
// Side effects:
//
// This routine does not return until valid state can be obtained
// This routine currently ignored the given TBUF element
//
// See also: n/a
//
// Example Usage:
// cur_state = tx_get_and_update_state(tbuf_elem)
//
__forceinline
static tx_state_t _spi4_tx_get_and_update_state(
unsigned int in_next_tbuf_elem)
{
tx_state_t ret_state;
// If EOP is true, get a new packet
if (tx_state.eop)
{
while (1)
{
// Dequeue a packet from the processing task
dl_source();
// Check for an empty queue
if (dlBufHandle.value != 0)
{
// The queue was not empty
tx_state.sop = 1;
tx_state.cur_buf_handle
= dlBufHandle;
tx_state.cur_mpacket_addr =
(__declspec(dram) unsigned char *)
Dl_BufGetData(dlBufHandle);
tx_state.remaining_length
= dlMeta.bufferSize;
break;
}
}
}
ret_state.cur_mpacket_addr = tx_state.cur_mpacket_addr;
ret_state.cur_buf_handle = tx_state.cur_buf_handle;
ret_state.sop = tx_state.sop;
// Update the global state for the next call to
// this macro. Check for EOP
if (tx_state.remaining_length <= TBUF_ELEM_SIZE)
{
tx_state.eop = 1;
ret_state.remaining_length =
tx_state.remaining_length;
}
else
{
tx_state.eop = 0;
ret_state.remaining_length = TBUF_ELEM_SIZE;
}
tx_state.cur_mpacket_addr += TBUF_ELEM_SIZE;
tx_state.remaining_length -= TBUF_ELEM_SIZE;
tx_state.sop = 0;
ret_state.eop = tx_state.eop;
return ret_state;
}
//-------------------------------------------------------------------
// spi4_tx
//
// Description:
// Segment and transmit buffers. This microblock operates
// as a context-pipeline and thus never returns
//
// Parameters:
// Outputs: n/a
// In/Outs: n/a
// Inputs: n/a
// Constants: n/a
// Labels: n/a
//
// Side effects:
//
// See also: n/a
//
//
void spi4_tx()
{
// This state is used during the transmission process to ensure
// that the TBUFs are used in order and without overrunning the
// hardware
// The index of the next tbuf element to be
// used
unsigned int next_tbuf_elem;
// The value of the last read to the
// tx_sequence number
unsigned int last_tx_seq;
// The number of TBUFs currently being
// transmitted. Based on the the last
// time the tx sequence number was read
unsigned int tbufs_in_flight;
SIGNAL_PAIR dram_to_tbuf_sig;
// State associated with the current mpacket
tx_state_t cur_state;
// Initialize the transmit state
next_tbuf_elem = 0;
tbufs_in_flight = 0;
last_tx_seq = 0;
// Setting the global EOP = 1 will force a dequeue
tx_state.eop = 1;
while(1)
{
// Check that the TBUF is available for use
while (tbufs_in_flight == NUM_TBUFS)
{
// We are out of TBUFs, wait for the
// sequence number to increase
_spi4_tx_update_tbufs_in_flight(
&tbufs_in_flight,
&last_tx_seq);
}
// Get the state (next mpacket) for the current
// TBUF element.
cur_state = _spi4_tx_get_and_update_state(
next_tbuf_elem);
// Move the next portion of the packet into
// the next tbuf
_spi4_tx_move_dram_to_tbuf(
next_tbuf_elem,
cur_state.cur_mpacket_addr,
cur_state.remaining_length,
&dram_to_tbuf_sig);
// As an optimization, if we have only one
// more TBUF available, then
// read and update the tbufs in fight during
// the transfer from DRAM to TBUF
if (tbufs_in_flight == (NUM_TBUFS - 1))
{
_spi4_tx_update_tbufs_in_flight(
&tbufs_in_flight,
&last_tx_seq);
}
// Wait for the TBUF to be filled
wait_for_all(&dram_to_tbuf_sig);
// Write the TBUF control word to validate
// the entry
_spi4_tx_validate_tbuf(
next_tbuf_elem,
cur_state.sop,
cur_state.eop,
cur_state.remaining_length);
// Update the global transmit state
next_tbuf_elem += TBUF_ELEM_SIZE / 64;
next_tbuf_elem &= 0x7f;
tbufs_in_flight++;
if (cur_state.eop)
{
// Free the buffer
Dl_BufDrop(cur_state.cur_buf_handle);
}
}
}
void exit(unsigned int code)
{
/* EMPTY */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -