龙芯软件开发(13)--配置南桥进入调试新天地

前面已经介绍到要初始化南桥,现在就来看看南桥是怎么样初始化,并且更重要的工作,就是怎么样初始化串口输出东西。先来看superio_init实现代码:

#define PCICONF_WRITEB(dev,func,reg,data) li a0,CFGADDR(dev,func,reg); li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); and a2,a0,0xffff; or a1,a2; srl a0,16; li a2,BONITO_BASE+BONITO_PCIMAP_CFG; sw a0,BONITO_PCIMAP_CFG(bonito); lw zero,BONITO_PCIMAP_CFG(bonito); or a0,zero,data; sb a0,(a1);

#define PCICONF_WRITEW(dev,func,reg,data) li a0,CFGADDR(dev,func,reg); li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); and a2,a0,0xffff; or a1,a2; srl a0,16; li a2,BONITO_BASE+BONITO_PCIMAP_CFG; sw a0,BONITO_PCIMAP_CFG(bonito); lw zero,BONITO_PCIMAP_CFG(bonito); or a0,zero,data; sw a0,(a1);
#define PCICONF_ORB(dev,func,reg,data) li a0,CFGADDR(dev,func,reg); li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); and a2,a0,0xffff; or a1,a2; srl a0,16; li a2,BONITO_BASE+BONITO_PCIMAP_CFG; sw a0,BONITO_PCIMAP_CFG(bonito); lw zero,BONITO_PCIMAP_CFG(bonito); lbu a2,(a1); ori a2,data; sw a0,BONITO_PCIMAP_CFG(bonito); lw zero,BONITO_PCIMAP_CFG(bonito); sb a2,(a1);
#define SUPERIO_WR(idx,data) li v0,BONITO_PCIIO_BASE_VA+0x3f0; or v1,zero,idx; sb v1,(v0); or v1,zero,data; sb v1,1(v0);

#define E2_EPP 2
#define E2_S1 (1«2)
#define E2_S2 (1«3)
#define E2_FLOPPY (1«4)

LEAF(superio_init)

PCICONF_WRITEW(PCI_IDSEL_VIA686B,0,4,7);
/*positive decode*/
PCICONF_ORB(PCI_IDSEL_VIA686B,0,0x81,0x80);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x83,0x80|0x1| 0x8);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,3);
/* enable RTC/PS2/KBC */
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x5A,7);

SUPERIO_WR(0xe2,E2_S2|E2_S1|E2_EPP|E2_FLOPPY) /*enable serial and floppy */
SUPERIO_WR(0xe3,0x3f0»2) /*floppy base address*/
SUPERIO_WR(0xe6,0x378»2) /*parallel port*/
SUPERIO_WR(0xe7,0x3f8»2) /*set serial port1 base addr 0x3f8*/
SUPERIO_WR(0xe8,0x2f8»2) /*set serial port2 base addr 0x2f8*/
SUPERIO_WR(0xee,0xc0) /* both ports on high speed*/

PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,1)
jr ra
nop
END(superio_init)

这段代码里,先用一个宏LEAF来简化子函数的定义,声明为“叶”函数,也就是说这个函数不再调用其它任可函数,如果又调用其它子函数就不叫做叶函数,这样做法,可以让汇编器尽可能地优化代码。接着下来,就看到这行代码:

PCICONF_WRITEW(PCI_IDSEL_VIA686B,0,4,7);

这里又调用一个宏实现往PCI总线上写一个字。现在把这个宏展开来看看,倒底是怎么样往PCI总线上写数据的,如下:
lui $a0,0x1
ori $a0,$a0,0x4
lui $a1,0xbfe8
andi $a2,$a0,0xffff
or $a1,$a1,$a2
在这里都是计算南桥的内存映射地址。

srl $a0,$a0,0x10
lui $a2,0x1fe0
ori $a2,$a2,0x118
sw $a0,280($s4)
lw $zero,280($s4)
到这里打开北桥的功能。(?)

li $a0,0x7
sw $a0,0($a1)
写数据到南桥的寄存器里。

PCICONF_ORB(PCI_IDSEL_VIA686B,0,0x81,0x80);
这句是选择正电压解码方式。

PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x83,0x80|0x1| 0x8);
这句是启用两个串口的功能和地址。

PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,3);
这句是启用SUPER IO的功能。

PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x5A,7);
这句是启用RTC实时时钟,PS2,KBC键盘控制器。
后面那些都是设置SUPER IO端口,分配地址。
后面那一行:
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,1)
这里重新启用南桥。
最后调用JR RA,就是返回子函数调用。

现在接着看串口的初始化,其实串口的初始化,就是设置怎么样接收和发送数据,以及用什么样的波特率。代码如下:

LEAF(initserial)

la v0, COM1_BASE_ADDR
1:
li v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
sb v1, NSREG(NS16550_FIFO)(v0)

li v1, CFCR_DLAB
sb v1, NSREG(NS16550_CFCR)(v0)

li v1, NS16550HZ/2/(16*CONS_BAUD)
sb v1, NSREG(NS16550_DATA)(v0)

srl v1, 8
sb v1, NSREG(NS16550_IER)(v0)

li v1, CFCR_8BITS
sb v1, NSREG(NS16550_CFCR)(v0)

li v1, MCR_DTR|MCR_RTS
sb v1, NSREG(NS16550_MCR)(v0)

li v1, 0x0
sb v1, NSREG(NS16550_IER)(v0)

nop

j ra
nop
END(initserial)
/*蔡军生 2006-12-30 于深圳 */
这里先获取COM1的基地址,是通过内存映射地址实现的。接着把FIFO的设置,设置CFCR寄存器,设置波特率,而波特率设置跟系统运行的频率 NS16550HZ有关。接着8位数据接收方式,设置中断允许寄存器。经过这样的初始化,就可以向串口写东西出来了,走出摸索前行的日子,进入调试的新天地。把串口接到另外一台电脑上,就可看到调试信息输出来,当然还可以实现源码级调试了。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License