📄 8251_8055_verilog.txt
字号:
/*****************************************************************************
通用串行异步收发器8251的Verilog HDL源代码
注意:作者不能保证本模块的完整和精确,使用本模块者如遇问题一切责任自负
******************************************************************************/
module I8251A ( dbus, rcd, gnd, txc_, write_, chipsel_, comdat_,
read_, rxrdy, txrdy, syndet, cts_, txe, txd,
clk, reset, dsr_,rts_,dtr_,rxc_,vcc);
/* timing constants ,for A. C. timing check, only non-zero times are
specified,in nano-sec */
/* read cycle */
`define TRR 250
`define TRD 200
`define TDF 100 // max. time used
/* write cycle */
`define TWW 250
`define TDW 150
`define TWD 20
`define TRV 6 // in terms of clock cycles
/* other timing */
`define TTXRDY 8 // 8 clock cycle
input rcd, //receive data
rxc_, //receive clock
txc_, //transmit clock
chipsel_, //chip selected when low
comdat_, //command /data_ select
read_,write_,
dsr_, // data set ready
cts_, // clear to send
reset, // reset when high
clk, // at least 30 times of the transmit/rexeibe data bit rates
gnd,
vcc;
output rxrdy, //receive data ready when high
txd, //transmit data lone
txrdy, //transmit buffer ready to accept another byte to transfer
txe, // transmit buffer empty
rts_, // request to send
dtr_; // data terminal ready
inout[7:0] dbus;
inout syndet; //outside synchonous detect or output to indicate syn det
supply0 gnd;
supply1 vcc;
reg txd, rxrdy, txe, dtr_, rts_;
reg [7:0] receivebuf, rdata, status;
//*****ADD BY FWN
reg [3:0] dflags;
reg [7:0] instance_id;
reg read,chipel_;
//*****
reg recvdrv, statusdrv;
// if recvdrv 1 dbus is driven by rdata
assign dbus = recvdrv ? rdata : 8'bz; //*****:,->;
assign dbus = statusdrv ? status : 8'bz ; //*****:->; assign abscent
reg [7:0] command,
tdata_out, // data being transmitted serially
tdata_hold, // data to be transmitted next if tdata_out is full
sync1,sync2, // synchronous data bytes
modreg;
and (txrdy, status[0], command[0], ~cts_);
reg transmitter_reset, // set to 1 upon a reset ,cleared upon write data
tdata_out_full, // 1 if data in tdata_out has not been transmitted.
tdata_hold_full, // 1 if data in tdata_hold has not been transferred
// to tdata_out for serial transmission.
tdata_hold_cts; // 1 if tdata_hold_full and it was cts when data
// was transferred to tdata_hold.
// 0 if tdata_hold is empty or is full but was
// filled while it was not cts.
reg tdata_out_wait; // 0 if a stop bit was just sent and we do not need
// to wait for a negedge on txc before transmitting
reg [7:0] syncmask;
nmos syndet_gate1(syndet,status[6], ~modreg[6]);
reg sync_to_receive; // 1(2) if looking for 1st(2nd) sync on rxd
reg syncs_received; // 1 if sync chars received, 0 if lookinf for sync
reg rec_sync_index; // indicating the syn. character to be matched
integer breakcount_period; // number of clock periods to count as break
reg sync_to_transmit; //1(2) if 1st(2nd) sync char should be sent next
reg [7:0] data_mask; //masks off the data bits (if char size is not 8)
// temporary registers
reg [1:0] csel; //indicates what next write means if comdat_=1:
//(0=mode instruction ,1=sync1,2=sync2,3=command)
reg [5:0] baudmx,
tbaudcnt,
rbaudcnt; // baud rate
reg[7:0] tstoptotal; // no. of tranmit clock pulses for stop bit (0 if sync mode
reg[3:0] databits; // no. of data bits in a character (5,6,7 or 8)
reg rdatain; // a data byte is read in if 1
reg was_cts_when_received; // 0:if cts_ was high when char was received
// 1:if cts_ was low wheb char was received
// (and so char was sent before shutdown)
event resete, start_receiver_e,hunt_sysnc1_e;
reg receive_in_progress;
event txende;
/*** COMMUNICATION ERRORS ***/
task frame_error;
begin
if(dflags[4])
$display("I8251A (%h)at %d: *** frame error ",instance_id,$time);
status[5]=1;
end
endtask
task parity_error;
begin
if(dflags[4])
$display("I8251A (%h) at %d : ***parity error data: %b",
instance_id, $time, receivebuf);
status[3]=1;
end
endtask
task overrun_error;
begin
if(dflags[4])
$display("I8251A (%h) at %d: *** oerrun error",instance_id,$time);
status[4]=1;
end
endtask
/*** TIMING VIOLATIONS ***/
integer time_dbus_setup,
time_write_begin,
time_write_end,
time_read_begin,
time_read_end,
between_write_clks; // to check between write recovery
reg reset_signal_in; //to check the reset signal pulse width
initial
begin
time_dbus_setup = -9999;
time_write_begin = -9999;
time_write_end = -9999;
time_read_begin = -9999;
time_read_end = -9999;
between_write_clks = `TRV; //start:TRV clk periods since last write
end
/*** Timing analysis for read cycles ***/
always @( negedge read_)
if (chipsel_==0)
begin
time_read_begin = $time;
read_address_watch;
end
/* Timing violation :read pulse must be TRR ns */
always @(posedge read_)
if (chipsel_==0)
begin
disable read_address_watch;
time_read_end = $time;
if(dflags[3] && (($time-time_read_begin) < `TRR))
$display("I8251A (%h) at %d: *** read pulse width violation",
instance_id, $time);
end
/* Timing violation :address (comdat_ and chipsel_) must be stable */
/* stable throughout read */
task read_address_watch;
@(comdat_ or chipsel_) //if the "address" changes
if (read ==0) // and read_ did not change at the same time
if (dflags[3])
$display("I8251A (%h) at %d : *** address hold error on ready",
instance_id, $time);
endtask
/** Timing analysis for write cycles **/
always @(negedge write_)
if (chipsel_==0)
begin
time_write_begin = $time;
write_address_watch;
end
/* Timing violation : read pulse must be TRR ns */
/* Timing violation : TDW ns bus setup time before posedge write_ */
/* Timing violation : TWD ns bus hold time after posedge write_ */
always @(posedge write_)
if (chipsel_==0)
begin
disable write_address_watch;
time_write_end=$time;
if(dflags[3] && (($time-time_write_begin) < `TWW))
$display("I8251A (%h) at %d: *** write pulse
width violation",instance_id,$time);
end
always @dbus
begin
time_dbus_setup = $time;
if(dflags[3] && (($time-time_write_end < `TWD)))
$display("I8251A (%h) at %d: *** datahold violation on write",
instance_id ,$time);
end
/* Timing violation: address (comdat_ and chipsel_ ) must be stable*/
/* stable throughout write */
task write_address_watch;
@(comdat_ or chipsel_ ) //if the "address" changes
if (write_==0) // and write_ did not change at the same time
if (dflags[3])
$display("I8251A (%h) at %d: *** address hold error on write",
instance_id , $time);
endtask
/* Timing violation: minimum of TRV clk cycles between writes */
always @( negedge write_ )
if ( chipel_==0 )
begin
time_write_begin=$time;
if(dflags[3] && between_write_clks < `TRV)
$display("I8251A (%h) at %d: ***between write recovery violation",
instance_id,$time);
end
always @(negedge write_)
repeat (`TRV) @(posedge clk)
between_write_clks = between_write_clks +1 ;
/**Timing analysis for reset sequence **/
/* Timing violation : reset pulse must be 6 clk cycles */
always @(posedge reset )
begin : reset_block
reset_signal_in=1;
repeat(6) @(posedge clk);
reset_signal_in=0;
//external reset
-> resete;
end
always @(negedge reset)
begin
if(dflags[3] && (reset_signal_in==1))
$display("I8251A (%h) at %d: *** reset pulse too short ", instance_id ,
$time);// lack of ;
disable reset_block;
end
/*** BEHAVIORAL DESCRIPTION ***/
/* Reset sequence */
initial
begin //power-on reset
reset_signal_in=0;
-> resete;
end
always @ resete
begin
if(dflags[5])
$display("I8251A (%h) at %d : performing reset sequence",
instance_id, $time);
csel=0;
transmitter_reset=1;
tdata_out_full=0;
tdata_out_wait=0;
tdata_hold_full=0;
tdata_hold_cts=0;
rdatain=0;
status=4; //only txe is set
txe=1;
statusdrv=0;
recvdrv=0;
txd=1; //line at mark state upon reset until data is transmitted
// assign not allowed for status ,etc.
rxrdy=0;
command=0;
dtr_=1;
rts_=1;
status[6]=0; // syndat is reset to output low
sync_to_transmit=1; //transmit sync char #1 when sync are transmit
sync_to_receive=1;
between_write_clks = `TRV;
receive_in_progress=0;
disable read_address_watch;
disable write_address_watch;
disable trans1;
disable trans2;
disable trans3;
disable trans4;
disable rcv_blk;
disable sync_hunt_blk;
disable double_sync_hunt_blk;
disable parity_sync_hunt_blk;
disable syn_receive_internal;
disable asyn_receive;
disable break_detect_blk;
disable break_delay_blk;
end
always @ ( negedge read_)
if (chipsel_==0)
begin
#(`TRD) // time for data to show on the data bus
if (comdat_==0) //8251A DATA ==> DATA BUS
begin
recvdrv=1;
rdatain=0; // no receive byte is ready
rxrdy=0;
status[1]=0;
end
else // 8251A STATUS ==> DATA BUS
begin
statusdrv=1;
if (modreg [1:0] ==2'b00) // if sync mode
status[6]=0; // reset syndet upon status ready
//note: is only reset upon reset or rxd=1 in async mode
end
end
always @ ( posedge read_)
begin
#(`TDF) //data from read stays on the bus after posedge read_
recvdrv=0;
statusdrv=0;
end
always @(negedge write_)
begin
if((chipsel_==0)&&(comdat_==0))
begin
txe=0;
status[2]=0;//transmitter not empty after receiving data
status[0]=0;//transmitter not ready after receiving data
end
end
always @(posedge write_) //read the command/data from the CPU
if (chipsel_==0)
begin
if (comdat_==0) //DATA BUS ==> 8251A DATA
begin
case (command[0] & ~ cts_)
0: //if it is not clear to send
begin
tdata_hold=dbus;
tdata_hold_full=1; //then mark the data as received and
tdata_hold_cts=0; // that it should be sent when cts
end
1: // if it is clear to send …
if(transmitter_reset) // … and this is 1st data since reset
begin
transmitter_reset=0;
tdata_out=dbus;
tdata_out_wait=1; // then wait for a negedge on txc
tdata_out_full=1; // and transmit the data
tdata_hold_full=0;
tdata_hold_cts=0;
repeat(`TTXRDY) @(posedge clk);
status[0]=1; // and set the txrdy status bit
end
else
begin
tdata_hold=dbus; // then mark the data as being receive
tdata_hold_full=1; // and that it should be transmitted
tdata_hold_cts=1; // it becomes not cts,
// but do not set the txrdy status bit
end
endcase
end
else // DATA BUS ==> CONTROL
begin
case (csel)
0: // case 0: MODE INSTRUCTION
begin
modreg=dbus;
if(modreg[1:0]==0) // synchronous mode
begin
csel=1;
baudmx=1;
tstoptotal=0; // no stop bit for synch. Op.
end
else //synchronous mode
begin
csel=3;
baudmx=1; //1X baud rate
if (modreg[1:0]==2'b10) baudmx=16;
if(modreg[1:0]==2'b11) baudmx=64;
//set up the stop bits in clocks
tstoptotal=baudmx;
if(modreg[7:6]==2'b10)
tstoptotal= tstoptotal + baudmx/2;
if(modreg[7:6]==2'b11)
tstoptotal= tstoptotal+tstoptotal;
end
databits=modreg[3:2]+5; // bits per char
data_mask=255 >> (3-modreg[3:2]);
end
1: //case 1: 1st SYNC CHAR -SYNC MODE
begin
sync1=dbus;
/* the syn. character will be adjusted to the most
significant bit to simplify syn, hunt,
syncmask is also set to test the top data bits */
case (modreg[3:2])
0:
begin
sync1=sync1<<3;
syncmask=8'b11111000;
end
1:
begin
sync1=sync1<< 2;
syncmask=8'b11111110;
end
2:
begin
sync1=sync1<< 1;
syncmask=8'b11111110;
end
3:
syncmask=8'b11111111;
endcase
if(modreg[7]==0)
csel=2; //if in double sync char mode, get 2 syncs
else
csel=3; // if in single sync char mode, get 1 sync
end
2: //case 2: 2nd SYNC CHAR - SYNC MODE
begin
sync2=dbus;
case (modreg[3:2])
0: sync2=sync2<< 3;
1: sync2=sync2<< 2;
2: sync2=sync2<< 1;
endcase
csel=3;
end
3: // case 3: COMMAND INSTRUCTION - SYNC/ASYNC MODE
begin
status[0]=0; // Trick:force delay txtdy pin if command[0]
command=dbus;
dtr_= ! command[1];
if(command[3]) // if send break command
assign txd=0; // set txd=0 (ignores/override ***** only
// candence synerngy support assign,deassign
else // later non-assign assignment
deassign txd;
if(command[4])
status[5:3]=0; //Clear Frame /Parity/Overrun
rts_= ! command[5];
if(command[6]) -> resete; //internal reset
if(modreg[1:0]==0 && command[7])
begin
// if sync mode and enter hunt
disable syn_receive_internal;
// disasble the sync receiver
disable syn_receive_external;
receivebuf=8'hff; // reset receive buffer 1's
-> start_receiver_e; // restart sync mode receiver
end
if(receive_in_progress==0)
-> start_receiver_e;
repeat(`TTXRDY) @(posedge clk);
status[0]=1;
end
endcase
end
end
reg [7:0] serial_data;
reg parity_bit;
always wait (tdata_out_full==1)
begin :trans1
if(dflags[1])
$display("I8251A (%h) at %d: transmitting data: %b",
instance_id,$time, tdata_out);
if(tdata_out_wait) // if the data arrived any old time
@(negedge txc_); // wait for a negedge on txc_
// but if a stop bit was just sent
// do not wait
serial_data=tdata_out;
if (tstoptotal != 0) // if async mode ...
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -