Bitcoin Forum
October 12, 2024, 09:04:11 PM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: « 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21] 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 »
  Print  
Author Topic: X6500 Custom FPGA Miner  (Read 219967 times)
thirdlight
Sr. Member
****
Offline Offline

Activity: 445
Merit: 250



View Profile
January 14, 2012, 11:47:35 AM
 #401

This one's just stalled:
Code:
2012-01-14 11:32:52 | (FPGA1) Golden nonce found
2012-01-14 11:32:52 | (FPGA1) accepted 7d1cf548L
2012-01-14 11:32:57 | Long-poll: new block 00000be305aa0b10
2012-01-14 11:32:57 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:32:58 | (FPGA1) jobqueue loaded (1)
2012-01-14 11:32:58 | (FPGA0) Job data loaded
2012-01-14 11:32:58 | (FPGA1) Job data loaded
2012-01-14 11:33:02 | (FPGA0) Golden nonce found
2012-01-14 11:33:03 | (FPGA0) accepted e9237680L
2012-01-14 11:33:13 | (FPGA0) Golden nonce found
2012-01-14 11:33:13 | (FPGA0) accepted 53c4cb99L
2012-01-14 11:33:18 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:33:18 | (FPGA1) jobqueue loaded (1)
2012-01-14 11:33:38 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:34:07 | Long-poll: new block 00000be305aa0b10
2012-01-14 11:34:07 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:34:07 | (FPGA1) jobqueue loaded (1)
310.62 MH/s | 0: 178/2/0 1.1%/0.0% | 1: 183/0/0 0.0%/0.0% | 1h24m | AH00WIBS
cttl-c gives:
Code:
2012-01-14 11:33:13 | (FPGA0) accepted 53c4cb99L
2012-01-14 11:33:18 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:33:18 | (FPGA1) jobqueue loaded (1)
2012-01-14 11:33:38 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:34:07 | Long-poll: new block 00000be305aa0b10
2012-01-14 11:34:07 | (FPGA0) jobqueue loaded (1)
2012-01-14 11:34:07 | (FPGA1) jobqueue loaded (1)
Exception in thread Thread-4:
Traceback (most recent call last):
  File "c:\python267\lib\threading.py", line 534, in __bootstrap_inner
    self.run()
  File "c:\python267\lib\threading.py", line 486, in run
    self.__target(*self.__args, **self.__kwargs)
  File "mine.py", line 274, in mineloop
    nonce = fpgaReadNonce(jtag[chain])
  File "mine.py", line 129, in fpgaReadNonce
    byte = fpgaReadByte(jtag)
  File "mine.py", line 115, in fpgaReadByte
    byte = bits2int(jtag.read_dr(bits))
  File "C:\x6500\xx\jtag.py", line 159, in read_dr
    return self.shift_dr(bits, read=True)
  File "C:\x6500\xx\jtag.py", line 156, in shift_dr
    return self.read_tdo(len(bits)+self._tckcount)[:len(bits)-self.current_part]

  File "C:\x6500\xx\jtag.py", line 163, in read_tdo
    data = self.ft232r.read_data(num)
  File "C:\x6500\xx\ft232r.py", line 229, in read_data
    self.flush()
  File "C:\x6500\xx\ft232r.py", line 207, in flush
    self._setAsyncMode()
  File "C:\x6500\xx\ft232r.py", line 197, in _setAsyncMode
    raise DeviceNotOpened()
DeviceNotOpened

 E                                                                           xce
ption in thread Thread-3:
Traceback (most recent call last):
  File "c:\python267\lib\threading.py", line 534, in __bootstrap_inner
    self.run()
  File "c:\python267\lib\threading.py", line 486, in run
    self.__target(*self.__args, **self.__kwargs)
  File "mine.py", line 260, in mineloop
    nonce = fpgaReadNonce(jtag[chain])
  File "mine.py", line 129, in fpgaReadNonce
    byte = fpgaReadByte(jtag)
  File "mine.py", line 115, in fpgaReadByte
    byte = bits2int(jtag.read_dr(bits))
  File "C:\x6500\xx\jtag.py", line 159, in read_dr
    return self.shift_dr(bits, read=True)
  File "C:\x6500\xx\jtag.py", line 156, in shift_dr
    return self.read_tdo(len(bits)+self._tckcount)[:len(bits)-self.current_part]

  File "C:\x6500\xx\jtag.py", line 163, in read_tdo
    data = self.ft232r.read_data(num)
  File "C:\x6500\xx\ft232r.py", line 246, in read_data
    while self.handle.getQueueStatus() < wrote:
AttributeError: 'NoneType' object has no attribute 'getQueueStatus'

2012-01-14 11:38:30 | Exiting...
Run Summary:
-------------
Device: 1
Serial: AH00WIBS
JTAG chain: 2
Number of FPGAs: 2
Running time: 1h26m
Getwork interval: 20 secs
FPGA 0:
  Accepted: 178
  Rejected: 2 (1.11%)
  Invalid: 0 (0.00%)
  Hashrate (all nonces): 149.56 MH/s
  Hashrate (valid nonces): 149.56 MH/s
  Hashrate (accepted shares): 147.90 MH/s
FPGA 1:
  Accepted: 183
  Rejected: 0 (0.00%)
  Invalid: 0 (0.00%)
  Hashrate (all nonces): 153.71 MH/s
  Hashrate (valid nonces): 153.71 MH/s
  Hashrate (accepted shares): 152.05 MH/s
Total hashrate for device: 303.28 MH/s / 303.28 MH/s / 299.95 MH/s
Hope it helps.

shad
Full Member
***
Offline Offline

Activity: 148
Merit: 100


View Profile
January 14, 2012, 12:47:04 PM
 #402

the exeption is as i thought
jtag can not read, i guess the interface is at use in that moment
is you system under great load?

i have a mine.py with a major change, i am running it since 10hours without troubles and at the usual MHash/sec
i believe it fixes the hangup,
as you have this problem more frequently than i, i would like so send you my modified mine.py for testing, maybe you could send me an PM with your mail-adress

15dUzJEUkxgjrtcvDSdsEDkXu7E7RCbNN3
thirdlight
Sr. Member
****
Offline Offline

Activity: 445
Merit: 250



View Profile
January 14, 2012, 01:02:24 PM
 #403

Thanks shad, will do.

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 14, 2012, 03:19:36 PM
 #404

Why do I have the feeling that shad and I have been working on the same thing again? Wink

I'm testing a rewrite with are no more Lock()'s and it appears to work. The cool thing about this is that it paves the way towards mining with many boards within one instance. I'll push my changes to Github after testing it for some more time, then it will be interesting to see what effect it has on the lock-up bug.

shad
Full Member
***
Offline Offline

Activity: 148
Merit: 100


View Profile
January 14, 2012, 03:30:47 PM
 #405

i gave the miningloop the chain_list, an called the 2 fpga's serial (in the miningloop) no more parallel, the lock is still in my code but it would also work if i delete does line Wink and the miningloop is still a thread

the thought behind is to make a miningloop per X6500, so i only need to add code for the board handling and some more adoption to be compatible with X6000
but i have not much motivation in making more card support since i only have one
my next step will be checking compatibily with P2P, i havn't looked into it but i heard it isn't working now or has it anybody running on P2P-pool?

maybe i should think about getting an github account so we could work together and not always working on the same idea in parallel Smiley


15dUzJEUkxgjrtcvDSdsEDkXu7E7RCbNN3
freshzive
Sr. Member
****
Offline Offline

Activity: 447
Merit: 250


View Profile
January 14, 2012, 05:04:33 PM
 #406

Haven't touched mine and they're still running stably Smiley Thanks for all the hard work fizzisist & shad, much appreciated.

320.75 MH/s | 0: 4922/19/28 0.4%/0.6% | 1: 5033/27/23 0.5%/0.5% | 1d13h17m | AH00WOWI
314.68 MH/s | 0: 4988/22/33 0.4%/0.7% | 1: 5048/29/22 0.6%/0.4% | 1d13h17m | AH00WOVL

Are there plans to mine from multiple FPGAs using one mine.py script? Or is that not feasible? Setting up two more boards today and I feel like monitoring 4 terminal tabs  is a little silly (and going to get more complex with larger farms).

Or maybe a simple script that could output speeds from multiple FPGAs at once (so they could all just be run in screens)? fizzisist how are you monitoring your farm?

thirdlight
Sr. Member
****
Offline Offline

Activity: 445
Merit: 250



View Profile
January 14, 2012, 06:31:30 PM
 #407

Stalled board running shad's mine.py:
Code:
2012-01-14 13:56:32 | (FPGA0) accepted 16602082L
2012-01-14 13:56:38 | (FPGA0) jobqueue loaded (1)
2012-01-14 13:56:38 | (FPGA0) Job data loaded
2012-01-14 13:56:38 | (FPGA1) jobqueue loaded (1)
2012-01-14 13:56:38 | (FPGA1) Job data loaded
2012-01-14 13:56:53 | (FPGA1) Golden nonce found
2012-01-14 13:56:53 | (FPGA1) accepted efdfb562L
2012-01-14 13:56:58 | (FPGA0) jobqueue loaded (1)
2012-01-14 13:56:58 | (FPGA0) Job data loaded
2012-01-14 13:56:58 | (FPGA1) jobqueue loaded (1)
2012-01-14 13:56:59 | (FPGA1) Job data loaded
2012-01-14 13:57:18 | (FPGA0) jobqueue loaded (1)
2012-01-14 13:57:19 | (FPGA1) jobqueue loaded (1)
2012-01-14 13:57:25 | Long-poll: new block 00000736dd68b0e6
2012-01-14 13:57:25 | (FPGA0) jobqueue loaded (1)
2012-01-14 13:57:25 | (FPGA1) jobqueue loaded (1)
2012-01-14 13:58:28 | Long-poll: new block 00000736dd68b0e6
2012-01-14 13:58:28 | (FPGA0) jobqueue loaded (1)
2012-01-14 18:19:01 | Exiting...
Run Summary:
E-xception in thread Thread-3:
Traceback (most recent call last):
  File "c:\python267\lib\threading.py", line 534, in __bootstrap_inner
    self.run()
  File "c:\python267\lib\threading.py", line 486, in run
    self.__target(*self.__args, **self.__kwargs)
  File "mine2.py", line 276, in mineloop
    nonce[chain] = fpgaReadNonce(jtag[chain])
  File "mine2.py", line 129, in fpgaReadNonce
    byte = fpgaReadByte(jtag)
  File "mine2.py", line 115, in fpgaReadByte
    byte = bits2int(jtag.read_dr(bits))
  File "C:\x6500\xx\jtag.py", line 159, in read_dr
    return self.shift_dr(bits, read=True)
  File "C:\x6500\xx\jtag.py", line 156, in shift_dr
    return self.read_tdo(len(bits)+self._tckcount)[:len(bits)-self.current_part]

  File "C:\x6500\xx\jtag.py", line 163, in read_tdo
    data = self.ft232r.read_data(num)
  File "C:\x6500\xx\ft232r.py", line 246, in read_data
    while self.handle.getQueueStatus() < wrote:
AttributeError: 'NoneType' object has no attribute 'getQueueStatus'

------------
Device: 1
Serial: AH00WIBS
JTAG chain: 2
Number of FPGAs: 2
Running time: 4h52m
Getwork interval: 20 secs
FPGA 0:
  Accepted: 70
  Rejected: 0 (0.00%)
  Invalid: 0 (0.00%)
  Hashrate (all nonces): 17.15 MH/s
  Hashrate (valid nonces): 17.15 MH/s
  Hashrate (accepted shares): 17.15 MH/s
FPGA 1:
  Accepted: 70
  Rejected: 0 (0.00%)
  Invalid: 0 (0.00%)
  Hashrate (all nonces): 17.15 MH/s
  Hashrate (valid nonces): 17.15 MH/s
  Hashrate (accepted shares): 17.15 MH/s
Total hashrate for device: 34.30 MH/s / 34.30 MH/s / 34.30 MH/s
Seconding appreciation to fizzisist & shad. Clever stuff!

shad
Full Member
***
Offline Offline

Activity: 148
Merit: 100


View Profile
January 14, 2012, 08:19:10 PM
 #408

 Angry
so you should switch to the version which is better running for you, but i think there isn't much a differenc in stability
is your Operation System on high CPU load when this happens?

we should try to fix it by adding the "# TODO: Add a timeout" to restart comunication
Quote
if starttime - time() > timeout then
 "send an error and jump back to a point with save data"

but this doesn't change my POV that the seriell calling is better then the threads


15dUzJEUkxgjrtcvDSdsEDkXu7E7RCbNN3
thirdlight
Sr. Member
****
Offline Offline

Activity: 445
Merit: 250



View Profile
January 14, 2012, 09:11:51 PM
 #409

Thanks shad.

I think you're right & there is little difference in stability.

The machine is a 1.6GHz atom, but ticks along at about 20% cpu use. OS is XP home.

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 12:15:03 AM
 #410

Angry
so you should switch to the version which is better running for you, but i think there isn't much a differenc in stability
is your Operation System on high CPU load when this happens?

we should try to fix it by adding the "# TODO: Add a timeout" to restart comunication
Quote
if starttime - time() > timeout then
 "send an error and jump back to a point with save data"

but this doesn't change my POV that the seriell calling is better then the threads



Yep, I think this is exactly the right idea. The thread locks might not have been to blame, but it really makes things easier to do without them. I'll add in that timeout now, test it out briefly, and push it to github. Thirdlight, would you mind testing it then? I'll send you a PM when it's ready. Thanks!

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 12:39:03 AM
 #411

Maybe just to remind it.

Please add a link to the executable of python 2.6.7 for the Windows users Smiley

http://www.activestate.com/activepython/downloads

Thanks

Thanks for the reminder, O_Shovah! Link added.

Are there plans to mine from multiple FPGAs using one mine.py script? Or is that not feasible? Setting up two more boards today and I feel like monitoring 4 terminal tabs  is a little silly (and going to get more complex with larger farms).

Or maybe a simple script that could output speeds from multiple FPGAs at once (so they could all just be run in screens)? fizzisist how are you monitoring your farm?

Yes, that's exactly the idea. In theory, this is pretty simple. The reality, though, is that presenting all of the information for that many FPGAs is tricky. Also, it would be best if you could reprogram the FPGAs from within that program, so you don't have to restart the whole thing every time you add a board. I'll be working on this.

Here's how I run a bunch of boards (12 at the moment). All of the mine.py instances are running within a gnu screen session. I'm using bitcoin-mining-proxy, and I have a munin plugin that queries the MySQL database for the proxy to make some nice graphs. This seems to work pretty well for me, but I would certainly like to cut down on the number of mine.py instances running.

freshzive
Sr. Member
****
Offline Offline

Activity: 447
Merit: 250


View Profile
January 15, 2012, 01:33:14 AM
 #412

Here's how I run a bunch of boards (12 at the moment). All of the mine.py instances are running within a gnu screen session. I'm using bitcoin-mining-proxy, and I have a munin plugin that queries the MySQL database for the proxy to make some nice graphs. This seems to work pretty well for me, but I would certainly like to cut down on the number of mine.py instances running.

Thanks for the info..going to try getting the mining-proxy setup on my Mac. It doesn't look like this provides alerts for down miners though?

2 more FPGAs in the mail today, now 4 running stably at 166mhz Smiley

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 06:49:11 AM
 #413

I pushed some changes to Github, including the timeout on read_data() that shad suggested. Hopefully this helps with the lock-up problem, but I've never had that happen to me so I need you guys to test it. Thanks!

Thanks for the info..going to try getting the mining-proxy setup on my Mac. It doesn't look like this provides alerts for down miners though?

The proxy is nice, but I think the combination with munin is the real win. Here's a screenshot of the FPGA.contract worker graph:



It should be possible to implement an interface compatible with munin without mining-proxy, directly in x6500-miner. I'll think about how to do this in a simple way.

Also, munin has the capability to send notifications based on some kind of threshold. I haven't set that up myself, but it would probably be a good idea.

shad
Full Member
***
Offline Offline

Activity: 148
Merit: 100


View Profile
January 15, 2012, 09:41:17 AM
Last edit: January 15, 2012, 10:23:54 AM by shad
 #414

i am looking at commit a312423 and i think there is a better solution
you have now a loop with: updateStatus()=>com with fpga0=>com with fpga1
the problem is that updatestatus blocks the comunication, an i guess we will put some more stuff into updateStatus if we make multi card support

i think the minethread was a good idea, but only 1 minethread per board with "com with fpga0=>com with fpga1"
Quote
   # impleent for card in number of cards
      # Start mining thread(s)
      minethread = Thread(target=mineloop, args=(chain_list,)) <== 2do chanlist per card? + ft232-interface
      minethread.daemon = True
      minethread.start()
      
      while True:
         time.sleep(1)
         logger.updateStatus()
         # TODO: implement a watchdog for the getwork thread
         if minethread is None or not minethread.isAlive():
            logger.log("Restarting minethread")
            minethread = Thread(target=mineloop, args=(chain_list,))
            minethread.daemon = True
            minethread.start()

15dUzJEUkxgjrtcvDSdsEDkXu7E7RCbNN3
thirdlight
Sr. Member
****
Offline Offline

Activity: 445
Merit: 250



View Profile
January 15, 2012, 10:07:14 AM
 #415

Thirdlight, would you mind testing it then?
Always happy to test - if I could do more, I would!

1 board running on current git code, I'll swap more over later.

Thanks for your work on this.  Smiley

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 12:58:24 PM
 #416

i am looking at commit a312423 and i think there is a better solution
you have now a loop with: updateStatus()=>com with fpga0=>com with fpga1
the problem is that updatestatus blocks the comunication, an i guess we will put some more stuff into updateStatus if we make multi card support

i think the minethread was a good idea, but only 1 minethread per board with "com with fpga0=>com with fpga1"

I think you're right. I probably threw out the baby with the bath water. To let you know, I just did a big reorganization that brings us a lot closer to multi-board support. It involved creating an FPGA class, which will moves of the FPGA communication out of mine.py and makes it much more modular, (instead of chain_list, we now have fpga_list, which can be iterated over and

I'm going to let it run while I sleep now, and if it looks hasn't crashed when I wake up I push it to Github then.

maybe i should think about getting an github account so we could work together and not always working on the same idea in parallel Smiley

Please do!

shad
Full Member
***
Offline Offline

Activity: 148
Merit: 100


View Profile
January 15, 2012, 02:02:23 PM
 #417

I think you're right. I probably threw out the baby with the bath water. To let you know, I just did a big reorganization that brings us a lot closer to multi-board support. It involved creating an FPGA class, which will moves of the FPGA communication out of mine.py and makes it much more modular, (instead of chain_list, we now have fpga_list, which can be iterated over and
yeah, i planed to make chain_list_per_board, as i want a thread per board, to get max usage of the weakest link which is the ft232, i was a little shocked about the much changes you made, i did my changes all in mine.py, so now my mine.py is a little incompatible, i wait until your next update
i think we agree with the way we want to go

maybe i should think about getting an github account so we could work together and not always working on the same idea in parallel Smiley

Please do!
i made one, i now have to understand it, hoping there is something out there like TortoiseSVN

15dUzJEUkxgjrtcvDSdsEDkXu7E7RCbNN3
fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 10:08:04 PM
 #418

I think you're right. I probably threw out the baby with the bath water. To let you know, I just did a big reorganization that brings us a lot closer to multi-board support. It involved creating an FPGA class, which will moves of the FPGA communication out of mine.py and makes it much more modular, (instead of chain_list, we now have fpga_list, which can be iterated over and
yeah, i planed to make chain_list_per_board, as i want a thread per board, to get max usage of the weakest link which is the ft232, i was a little shocked about the much changes you made, i did my changes all in mine.py, so now my mine.py is a little incompatible, i wait until your next update
i think we agree with the way we want to go

Ok, then you are going to be even more shocked by the latest changes (pushed to Github minutes ago). Sorry to break your changes.

The changes are drastic, but I think necessary to keep everything organized as the code grows. This is an incremental step towards multi-board support, but it lays a lot of groundwork. Please check it out!

i made one, i now have to understand it, hoping there is something out there like TortoiseSVN

I typically just use the git command line tool in linux, but have used it on Windows as well. There's a git GUI, which should get it closer to TortoiseSVN, although I haven't used it. Check out the Windows help on Github which looks very helpful.

O_Shovah
Sr. Member
****
Offline Offline

Activity: 410
Merit: 252


Watercooling the world of mining


View Profile
January 15, 2012, 10:43:57 PM
 #419

Hello I'm curently trying on the new code.
Code:
2012-01-15 23:33:54 | Device 0 opened (AH00WQ10)
2012-01-15 23:33:54 | Connected to 2 FPGAs
2012-01-15 23:33:54 | Connected to server
2012-01-15 23:33:54 | 0: Error getting work! Retrying...
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python26\lib\threading.py", line 534, in __bootstrap_inner
    self.run()
  File "C:\Python26\lib\threading.py", line 486, in run
    self.__target(*self.__args, **self.__kwargs)
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\rpcClie
nt.py", line 208, in getwork_loop
    self.getNewJob(fpga)
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\rpcClie
nt.py", line 174, in getNewJob
    self.logger.log("%d: Error getting work! Retrying..." % fpga.id)
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\Console
Logger.py", line 352, in log
    self.updateStatus(True)
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\Console
Logger.py", line 283, in updateStatus
    acc = sum([fpga.accepted_count for fpga in fpga_list])
NameError: global name 'fpga_list' is not defined

Traceback (most recent call last):
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\mine.py
", line 204, in <module>
    logger.updateStatus()
  File "C:\Users\O_Shovah\Desktop\FPGA_x6500\fizzisist_x6500_miner_0.2.8\Console
Logger.py", line 283, in updateStatus
    acc = sum([fpga.accepted_count for fpga in fpga_list])
NameError: global name 'fpga_list' is not defined

I dont have the time to look into the code for that error.
I hope it helps some more familiar with it.

fizzisist (OP)
Hero Member
*****
Offline Offline

Activity: 720
Merit: 525



View Profile WWW
January 15, 2012, 11:24:36 PM
 #420

I dont have the time to look into the code for that error.
I hope it helps some more familiar with it.

Thanks for catching that, O_Shovah! It turns out that I forgot to test it without the 'verbose' option and screwed something up there. Should be fixed now!

Pages: « 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [21] 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 »
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!